Skip to content

Commit d814917

Browse files
authored
Merge branch 'main' into conico/rnd-8621-integration-markdown
2 parents 8d61785 + 3e40b4d commit d814917

File tree

10 files changed

+201
-110
lines changed

10 files changed

+201
-110
lines changed

.changeset/ready-ducks-burn.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@gitbook/react-openapi': patch
3+
---
4+
5+
Fix OpenAPI basic auth placeholder

bun.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"devDependencies": {
88
"@biomejs/biome": "^1.9.4",
99
"@changesets/cli": "^2.29.7",
10-
"turbo": "^2.5.8",
10+
"turbo": "^2.6.1",
1111
"vercel": "^39.4.2",
1212
},
1313
},
Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
1-
import { type DocumentBlockContentRef, SiteInsightsLinkPosition } from '@gitbook/api';
1+
import {
2+
type ContentRef,
3+
type DocumentBlockContentRef,
4+
SiteInsightsLinkPosition,
5+
} from '@gitbook/api';
26

3-
import { Card } from '@/components/primitives';
4-
import { type ResolvedContentRef, resolveContentRef } from '@/lib/references';
7+
import { Card, type CardProps } from '@/components/primitives';
8+
import {
9+
type ResolvedContentRef,
10+
resolveContentRef,
11+
resolveContentRefFallback,
12+
} from '@/lib/references';
513

614
import type { BlockProps } from './Block';
15+
import { NotFoundRefHoverCard } from './NotFoundRefHoverCard';
716

817
export async function BlockContentRef(props: BlockProps<DocumentBlockContentRef>) {
918
const { block, context, style } = props;
@@ -16,51 +25,45 @@ export async function BlockContentRef(props: BlockProps<DocumentBlockContentRef>
1625
: null;
1726

1827
if (!resolved) {
19-
return null;
28+
const fallback = resolveContentRefFallback(block.data.ref);
29+
if (!fallback) {
30+
return null;
31+
}
32+
return (
33+
<NotFoundRefHoverCard context={context}>
34+
<BlockContentRefCard
35+
contentRef={block.data.ref}
36+
resolved={fallback}
37+
style={style}
38+
/>
39+
</NotFoundRefHoverCard>
40+
);
2041
}
2142

22-
const isContentInOtherSpace =
23-
context.contentContext?.space &&
24-
'space' in block.data.ref &&
25-
context.contentContext.space.id !== block.data.ref.space;
26-
const kind = block?.data?.ref?.kind;
27-
if ((resolved.active && kind === 'space') || isContentInOtherSpace) {
28-
return <SpaceRefCard {...props} resolved={resolved} />;
29-
}
43+
return <BlockContentRefCard contentRef={block.data.ref} resolved={resolved} style={style} />;
44+
}
3045

46+
function BlockContentRefCard(
47+
props: {
48+
resolved: ResolvedContentRef;
49+
contentRef: ContentRef;
50+
} & Omit<CardProps, 'href' | 'title'>
51+
) {
52+
const { ref, resolved, contentRef, ...rest } = props;
3153
return (
3254
<Card
55+
ref={ref}
3356
leadingIcon={resolved.icon ? resolved.icon : null}
3457
href={resolved.href}
3558
title={resolved.text}
36-
style={style}
3759
insights={{
3860
type: 'link_click',
3961
link: {
40-
target: block.data.ref,
62+
target: contentRef,
4163
position: SiteInsightsLinkPosition.Content,
4264
},
4365
}}
44-
/>
45-
);
46-
}
47-
48-
async function SpaceRefCard(
49-
props: { resolved: ResolvedContentRef } & BlockProps<DocumentBlockContentRef>
50-
) {
51-
const { context, style, resolved } = props;
52-
const spaceId = context.contentContext?.space.id;
53-
54-
if (!spaceId) {
55-
return null;
56-
}
57-
58-
return (
59-
<Card
60-
href={resolved.href}
61-
title={resolved.text}
62-
postTitle={resolved.subText}
63-
style={style}
66+
{...rest}
6467
/>
6568
);
6669
}
Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,29 @@
1-
import { resolveContentRef } from '@/lib/references';
1+
import { resolveContentRef, resolveContentRefFallback } from '@/lib/references';
22
import * as api from '@gitbook/api';
33
import type { IconName } from '@gitbook/icons';
44
import { Button } from '../primitives';
55
import type { InlineProps } from './Inline';
6+
import { NotFoundRefHoverCard } from './NotFoundRefHoverCard';
67

78
export async function InlineButton(props: InlineProps<api.DocumentInlineButton>) {
89
const { inline, context } = props;
910

10-
if (!context.contentContext) {
11-
throw new Error('InlineButton requires a contentContext');
12-
}
13-
14-
const resolved = await resolveContentRef(inline.data.ref, context.contentContext);
11+
const resolved = context.contentContext
12+
? await resolveContentRef(inline.data.ref, context.contentContext)
13+
: null;
1514

16-
if (!resolved) {
17-
return null;
18-
}
15+
const href = resolved?.href ?? resolveContentRefFallback(inline.data.ref)?.href;
1916

20-
return (
17+
const inlineElement = (
2118
// Set the leading to have some vertical space between adjacent buttons
2219
<span className="inline-button leading-12 [&:has(+.inline-button)]:mr-2">
2320
<Button
24-
href={resolved.href}
21+
href={href}
2522
label={inline.data.label}
2623
// TODO: use a variant specifically for user-defined buttons.
2724
variant={inline.data.kind}
2825
className="leading-normal"
26+
disabled={href === undefined}
2927
icon={inline.data.icon as IconName | undefined}
3028
insights={{
3129
type: 'link_click',
@@ -37,4 +35,10 @@ export async function InlineButton(props: InlineProps<api.DocumentInlineButton>)
3735
/>
3836
</span>
3937
);
38+
39+
if (!resolved) {
40+
return <NotFoundRefHoverCard context={context}>{inlineElement}</NotFoundRefHoverCard>;
41+
}
42+
43+
return inlineElement;
4044
}

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

Lines changed: 63 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1-
import { type DocumentInlineLink, SiteInsightsLinkPosition } from '@gitbook/api';
1+
import { type ContentRef, type DocumentInlineLink, SiteInsightsLinkPosition } from '@gitbook/api';
22

33
import { getSpaceLanguage, tString } from '@/intl/server';
44
import { type TranslationLanguage, languages } from '@/intl/translations';
5-
import { type ResolvedContentRef, resolveContentRef } from '@/lib/references';
5+
import {
6+
type ResolvedContentRef,
7+
resolveContentRef,
8+
resolveContentRefFallback,
9+
} from '@/lib/references';
610
import { Icon } from '@gitbook/icons';
7-
import { HoverCard, HoverCardRoot, HoverCardTrigger, StyledLink } from '../../primitives';
11+
import { StyledLink } from '../../primitives';
812
import type { InlineProps } from '../Inline';
913
import { Inlines } from '../Inlines';
14+
import { NotFoundRefHoverCard } from '../NotFoundRefHoverCard';
1015
import { InlineLinkTooltip } from './InlineLinkTooltip';
1116

1217
export async function InlineLink(props: InlineProps<DocumentInlineLink>) {
@@ -18,52 +23,75 @@ export async function InlineLink(props: InlineProps<DocumentInlineLink>) {
1823
resolveAnchorText: false,
1924
})
2025
: null;
26+
2127
const { contentContext } = context;
2228

23-
const language = contentContext ? getSpaceLanguage(contentContext) : languages.en;
29+
const inlinesElement = (
30+
<Inlines
31+
context={context}
32+
document={document}
33+
nodes={inline.nodes}
34+
ancestorInlines={[...ancestorInlines, inline]}
35+
/>
36+
);
2437

25-
if (!contentContext || !resolved) {
38+
if (!resolved) {
39+
const fallback = resolveContentRefFallback(inline.data.ref);
2640
return (
27-
<HoverCardRoot>
28-
<HoverCardTrigger>
29-
<span className="cursor-not-allowed underline">
30-
<Inlines
31-
context={context}
32-
document={document}
33-
nodes={inline.nodes}
34-
ancestorInlines={[...ancestorInlines, inline]}
35-
/>
36-
</span>
37-
</HoverCardTrigger>
38-
<HoverCard className="flex flex-col gap-1 p-4">
39-
<div className="flex items-center gap-2">
40-
<Icon icon="ban" className="size-4 text-tint-subtle" />
41-
<h5 className="font-semibold">{tString(language, 'notfound_title')}</h5>
42-
</div>
43-
<p className="text-sm text-tint">{tString(language, 'notfound_link')}</p>
44-
</HoverCard>
45-
</HoverCardRoot>
41+
<NotFoundRefHoverCard context={context}>
42+
{fallback ? (
43+
<InlineLinkAnchor href={fallback.href} contentRef={inline.data.ref} isExternal>
44+
{inlinesElement}
45+
</InlineLinkAnchor>
46+
) : (
47+
<span className="cursor-not-allowed underline">{inlinesElement}</span>
48+
)}
49+
</NotFoundRefHoverCard>
4650
);
4751
}
48-
const isExternal = inline.data.ref.kind === 'url';
49-
const isMailto = resolved.href.startsWith('mailto:');
50-
const content = (
51-
<StyledLink
52+
const anchorElement = (
53+
<InlineLinkAnchor
5254
href={resolved.href}
55+
contentRef={inline.data.ref}
56+
isExternal={inline.data.ref.kind === 'url'}
57+
>
58+
{inlinesElement}
59+
</InlineLinkAnchor>
60+
);
61+
62+
if (context.withLinkPreviews) {
63+
const language = contentContext ? getSpaceLanguage(contentContext) : languages.en;
64+
65+
return (
66+
<InlineLinkTooltipWrapper inline={inline} language={language} resolved={resolved}>
67+
{anchorElement}
68+
</InlineLinkTooltipWrapper>
69+
);
70+
}
71+
72+
return anchorElement;
73+
}
74+
75+
function InlineLinkAnchor(props: {
76+
href: string;
77+
contentRef: ContentRef;
78+
isExternal?: boolean;
79+
children: React.ReactNode;
80+
}) {
81+
const { href, isExternal, contentRef, children } = props;
82+
const isMailto = href.startsWith('mailto:');
83+
return (
84+
<StyledLink
85+
href={href}
5386
insights={{
5487
type: 'link_click',
5588
link: {
56-
target: inline.data.ref,
89+
target: contentRef,
5790
position: SiteInsightsLinkPosition.Content,
5891
},
5992
}}
6093
>
61-
<Inlines
62-
context={context}
63-
document={document}
64-
nodes={inline.nodes}
65-
ancestorInlines={[...ancestorInlines, inline]}
66-
/>
94+
{children}
6795
{isMailto ? (
6896
<Icon
6997
icon="envelope"
@@ -77,16 +105,6 @@ export async function InlineLink(props: InlineProps<DocumentInlineLink>) {
77105
) : null}
78106
</StyledLink>
79107
);
80-
81-
if (context.withLinkPreviews) {
82-
return (
83-
<InlineLinkTooltipWrapper inline={inline} language={language} resolved={resolved}>
84-
{content}
85-
</InlineLinkTooltipWrapper>
86-
);
87-
}
88-
89-
return content;
90108
}
91109

92110
/**
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { getSpaceLanguage, tString } from '@/intl/server';
2+
import { languages } from '@/intl/translations';
3+
import { Icon } from '@gitbook/icons';
4+
import type { DocumentContextProps } from '../DocumentView';
5+
import { HoverCard, HoverCardRoot, HoverCardTrigger } from '../primitives';
6+
7+
/**
8+
* Hover card displayed for a link not found.
9+
*/
10+
export function NotFoundRefHoverCard(
11+
props: DocumentContextProps & {
12+
children: React.ReactNode;
13+
}
14+
) {
15+
const {
16+
context: { contentContext },
17+
children,
18+
} = props;
19+
const language = contentContext ? getSpaceLanguage(contentContext) : languages.en;
20+
return (
21+
<HoverCardRoot>
22+
<HoverCardTrigger>{children}</HoverCardTrigger>
23+
<HoverCard className="flex flex-col gap-1 p-4">
24+
<div className="flex items-center gap-2">
25+
<Icon icon="ban" className="size-4 text-tint-subtle" />
26+
<h5 className="font-semibold">{tString(language, 'notfound_title')}</h5>
27+
</div>
28+
<p className="text-sm text-tint">{tString(language, 'notfound_link')}</p>
29+
</HoverCard>
30+
</HoverCardRoot>
31+
);
32+
}

packages/gitbook/src/components/primitives/Card.tsx

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,40 @@ import { Icon } from '@gitbook/icons';
22

33
import { type ClassValue, tcls } from '@/lib/tailwind';
44

5-
import { Link, type LinkInsightsProps } from './Link';
5+
import { Link, type LinkInsightsProps, type LinkProps } from './Link';
66

7-
export async function Card(
7+
export type CardProps = {
8+
href: string;
9+
leadingIcon?: React.ReactNode;
10+
preTitle?: string;
11+
title: string;
12+
postTitle?: string;
13+
style?: ClassValue;
14+
} & LinkInsightsProps &
15+
Omit<LinkProps, 'href' | 'className' | 'classNames' | 'insights' | 'style'>;
16+
17+
export function Card(
818
props: {
919
href: string;
1020
leadingIcon?: React.ReactNode;
1121
preTitle?: string;
1222
title: string;
1323
postTitle?: string;
1424
style?: ClassValue;
15-
} & LinkInsightsProps
25+
} & LinkInsightsProps &
26+
Omit<LinkProps, 'href' | 'className' | 'classNames' | 'insights' | 'style'>
1627
) {
17-
const { title, leadingIcon, href, preTitle, postTitle, style, insights } = props;
28+
const { ref, title, leadingIcon, href, preTitle, postTitle, style, insights, ...rest } = props;
1829

1930
return (
20-
<Link href={href} className={tcls(style)} classNames={['CardStyles']} insights={insights}>
31+
<Link
32+
ref={ref}
33+
href={href}
34+
className={tcls(style)}
35+
classNames={['CardStyles']}
36+
insights={insights}
37+
{...rest}
38+
>
2139
{leadingIcon}
2240
<span className={tcls('flex', 'flex-col', 'flex-1')}>
2341
{preTitle ? (

0 commit comments

Comments
 (0)