diff --git a/bun.lockb b/bun.lockb index f6fe2ed7c..9b88c5248 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 6548ea7e1..82112485a 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@geist-ui/icons": "^1.0.2", "@gitbook/api": "^0.11.1", "@readme/openapi-parser": "^2.5.0", + "assert-never": "^1.2.1", "bun-types": "^1.0.7", "next": "13.5.6", "react": "^18", diff --git a/src/components/DocumentView/Block.tsx b/src/components/DocumentView/Block.tsx index ba5e48e1f..1a781ef1c 100644 --- a/src/components/DocumentView/Block.tsx +++ b/src/components/DocumentView/Block.tsx @@ -13,6 +13,7 @@ import { Tabs } from './Tabs'; import { Expandable } from './Expandable'; import { Table } from './Table'; import { Swagger } from './Swagger'; +import { Embed } from './Embed'; export interface BlockProps extends DocumentContextProps { block: T; @@ -49,6 +50,8 @@ export function Block(props: BlockProps) { return ; case 'swagger': return ; + case 'embed': + return ; default: return
Unsupported block {block.type}
; } diff --git a/src/components/DocumentView/Embed.tsx b/src/components/DocumentView/Embed.tsx new file mode 100644 index 000000000..856f2681b --- /dev/null +++ b/src/components/DocumentView/Embed.tsx @@ -0,0 +1,30 @@ +import { tcls } from '@/lib/tailwind'; +import { BlockProps } from './Block'; +import { Blocks } from './Blocks'; +import { DocumentBlockEmbed } from '@gitbook/api'; +import { api } from '@/lib/api'; + +export async function Embed(props: BlockProps) { + const { block, style, ...contextProps } = props; + + const { data: embed } = await api().urls.getEmbedByUrl({ url: block.data.url }); + + // TODO caption + + if (embed.type === 'rich') { + return ( +
+ ); + } else { + return ( +
+
{embed.title}
+
+ ); + } +} diff --git a/src/components/DocumentView/Heading.tsx b/src/components/DocumentView/Heading.tsx index 20713b285..f4655fd76 100644 --- a/src/components/DocumentView/Heading.tsx +++ b/src/components/DocumentView/Heading.tsx @@ -3,19 +3,18 @@ import IconHash from '@geist-ui/icons/hash'; import { BlockProps } from './Block'; import { Inlines } from './Inlines'; import { DocumentBlockHeading } from '@gitbook/api'; +import { getBlockTextStyle } from './spacing'; export function Heading(props: BlockProps) { const { block, style, ...contextProps } = props; - const headingStyle = STYLES[block.type]; + const textStyle = getBlockTextStyle(block); + const Tag = TAGS[block.type]; const id = block.meta?.id ?? ''; return ( - +
) { 'group-hover:opacity-100', 'group-focus:opacity-100', 'lg:flex', - headingStyle.lineHeight, + textStyle.lineHeight, )} > ) {
-
+ ); } -const STYLES = { - 'heading-1': { - tag: 'h2', - lineHeight: 'h-9', - className: ['text-3xl', 'font-semibold'], - }, - 'heading-2': { - tag: 'h3', - lineHeight: 'h-8', - className: ['text-2xl', 'font-semibold'], - }, - 'heading-3': { - tag: 'h4', - lineHeight: 'h-6', - className: ['text-base', 'font-semibold'], - }, +const TAGS: { [type in DocumentBlockHeading['type']]: string } = { + // The h1 is reserved for the page title + 'heading-1': 'h2', + 'heading-2': 'h3', + 'heading-3': 'h4', }; diff --git a/src/components/DocumentView/Hint.tsx b/src/components/DocumentView/Hint.tsx index 44bddcc2e..c08001fcb 100644 --- a/src/components/DocumentView/Hint.tsx +++ b/src/components/DocumentView/Hint.tsx @@ -1,17 +1,75 @@ -import { tcls } from '@/lib/tailwind'; +import { ClassValue, tcls } from '@/lib/tailwind'; import { BlockProps } from './Block'; import { Blocks } from './Blocks'; import { DocumentBlockHint } from '@gitbook/api'; +import IconInfo from '@geist-ui/icons/info'; +import IconAlertTriangle from '@geist-ui/icons/alertTriangle'; +import IconAlertCircle from '@geist-ui/icons/alertCircle'; +import IconCheckInCircle from '@geist-ui/icons/checkInCircle'; +import { getBlockTextStyle } from './spacing'; export function Hint(props: BlockProps) { const { block, style, ...contextProps } = props; + const hintStyle = HINT_STYLES[block.data.style]; + const firstLine = getBlockTextStyle(block.nodes[0]); return ( -
-
-
{/* TODO icon */} +
+
+
); } + +const HINT_STYLES: { + [style in DocumentBlockHint['data']['style']]: { + icon: React.ComponentType<{ className?: string }>; + iconStyle: ClassValue; + style: ClassValue; + }; +} = { + info: { + icon: IconInfo, + iconStyle: ['text-blue-500'], + style: ['border-blue-500'], + }, + warning: { + icon: IconAlertCircle, + iconStyle: ['text-orange-500'], + style: ['border-orange-500'], + }, + danger: { + icon: IconAlertTriangle, + iconStyle: ['text-red-500'], + style: ['border-red-500'], + }, + success: { + icon: IconCheckInCircle, + iconStyle: ['text-green-500'], + style: ['border-green-500'], + }, +}; diff --git a/src/components/DocumentView/spacing.ts b/src/components/DocumentView/spacing.ts new file mode 100644 index 000000000..6505d5e7c --- /dev/null +++ b/src/components/DocumentView/spacing.ts @@ -0,0 +1,49 @@ +import { ClassValue } from '@/lib/tailwind'; +import { DocumentBlockListItem, DocumentBlocksEssentials } from '@gitbook/api'; +import { assertNever } from 'assert-never'; + +/** + * Get the line height of a block + */ +export function getBlockTextStyle(block: DocumentBlocksEssentials | DocumentBlockListItem): { + /** Tailwind class for the text size */ + textSize: ClassValue; + /** Tailwind class for the height (h-*) */ + lineHeight: ClassValue; +} { + switch (block.type) { + case 'paragraph': + return { + textSize: ['text-base'], + lineHeight: 'h-6', + }; + case 'heading-1': + return { + textSize: ['text-3xl', 'font-semibold'], + lineHeight: 'h-9', + }; + case 'heading-2': + return { + textSize: ['text-2xl', 'font-semibold'], + lineHeight: 'h-8', + }; + case 'heading-3': + return { + textSize: ['text-base', 'font-semibold'], + lineHeight: 'h-6', + }; + case 'divider': + return { + textSize: [], + lineHeight: 'h-6', + }; + case 'list-ordered': + case 'list-tasks': + case 'list-unordered': + return getBlockTextStyle(block.nodes[0]); + case 'list-item': + return getBlockTextStyle(block.nodes[0]); + default: + assertNever(block); + } +}