Try more blocks

This commit is contained in:
Samy Pessé
2023-11-01 13:41:14 -04:00
parent 12da9e27bb
commit 07ad0d3909
17 changed files with 156 additions and 37 deletions
+33 -13
View File
@@ -1,19 +1,39 @@
# GitBook
## TODOs
Next.js application to render GitBook published content.
- OG cover image
- [ ] Redirect to the customization one if any
- Favicon
- [ ] Redirect to the customization one if any
- [ ] Render one
- Blocks
- [ ] Code block syntax highlighting
- Theme
- [ ] Toggler
- Page feedback
- Edit on GitHub
- Header links
## Development
#### Installation
Clone the repository and use [Bun](https://bun.sh/) to install dependencies and run the local development server.
```
bun install
```
#### Configuration
To develop and test a GitBook space locally, first create a [GitBook API token](https://app.gitbook.com/account/developer) and put it in a `.env.local` file:
```
GITBOOK_TOKEN=gb_api_abc
```
#### Start the local server
Run the Next.js development server.
```
bun dev
```
Then open the space in your web browser: `http://localhost:3000/<space>/`.
#### Other commands
- `bun format`: format the code
- `bun lint`: lint the code
## Differences
BIN
View File
Binary file not shown.
+1
View File
@@ -14,6 +14,7 @@
"@geist-ui/icons": "^1.0.2",
"@gitbook/api": "^0.11.1",
"@readme/openapi-parser": "^2.5.0",
"ajv": "^8.12.0",
"assert-never": "^1.2.1",
"bun-types": "^1.0.7",
"katex": "^0.16.9",
+20 -3
View File
@@ -5,7 +5,6 @@ import { ListOrdered } from './ListOrdered';
import { ListUnordered } from './ListUnordered';
import { ListItem } from './ListItem';
import { CodeBlock } from './CodeBlock';
import { tcls } from '@/lib/tailwind';
import { Hint } from './Hint';
import { DocumentContextProps } from './DocumentView';
import { Images } from './Images';
@@ -17,6 +16,8 @@ import { Embed } from './Embed';
import { Quote } from './Quote';
import {
DocumentBlockCode,
DocumentBlockDivider,
DocumentBlockDrawing,
DocumentBlockEmbed,
DocumentBlockExpandable,
DocumentBlockFile,
@@ -33,10 +34,15 @@ import {
DocumentBlockSwagger,
DocumentBlockTable,
DocumentBlockTabs,
DocumentBlockTabsItem,
DocumentBlockTaskListItem,
} from '@gitbook/api';
import { BlockMath } from './Math';
import { File } from './File';
import { ListTasks } from './ListTasks';
import { Divider } from './Divider';
import assertNever from 'assert-never';
import { Drawing } from './Drawing';
export type SupportedBlock =
| DocumentBlockParagraph
@@ -56,10 +62,15 @@ export type SupportedBlock =
| DocumentBlockEmbed
| DocumentBlockQuote
| DocumentBlockMath
| DocumentBlockFile;
| DocumentBlockFile
| DocumentBlockDivider
| DocumentBlockDrawing;
export type AncestorBlock = SupportedBlock | DocumentBlockTabsItem;
export interface BlockProps<Block extends SupportedBlock> extends DocumentContextProps {
block: Block;
ancestorBlocks: AncestorBlock[];
style?: ClassValue;
}
@@ -77,6 +88,8 @@ export function Block<T extends SupportedBlock>(props: BlockProps<T>) {
return <ListOrdered {...props} {...contextProps} block={block} />;
case 'list-unordered':
return <ListUnordered {...props} {...contextProps} block={block} />;
case 'list-tasks':
return <ListTasks {...props} {...contextProps} block={block} />;
case 'list-item':
return <ListItem {...props} {...contextProps} block={block} />;
case 'code':
@@ -101,7 +114,11 @@ export function Block<T extends SupportedBlock>(props: BlockProps<T>) {
return <BlockMath {...props} {...contextProps} block={block} />;
case 'file':
return <File {...props} {...contextProps} block={block} />;
case 'divider':
return <Divider {...props} {...contextProps} block={block} />;
case 'drawing':
return <Drawing {...props} {...contextProps} block={block} />;
default:
return <div className={tcls(style)}>Unsupported block {block.type}</div>;
assertNever(block);
}
}
+5 -2
View File
@@ -1,12 +1,15 @@
import { Block } from './Block';
import { AncestorBlock, Block, SupportedBlock } from './Block';
import { DocumentContextProps } from './DocumentView';
import { tcls, ClassValue } from '@/lib/tailwind';
export function Blocks<T extends { key?: string }, Tag extends React.ElementType = 'div'>(
export function Blocks<T extends SupportedBlock, Tag extends React.ElementType = 'div'>(
props: DocumentContextProps & {
/** Blocks to render */
nodes: T[];
/** Ancestors of the blocks */
ancestorBlocks: AncestorBlock[];
/** HTML tag to use */
tag?: Tag;
+9
View File
@@ -0,0 +1,9 @@
import { DocumentBlockDivider } from '@gitbook/api';
import { BlockProps } from './Block';
import { tcls } from '@/lib/tailwind';
export function Divider(props: BlockProps<DocumentBlockDivider>) {
const { style } = props;
return <hr className={tcls(style)} />;
}
+9 -1
View File
@@ -18,5 +18,13 @@ export function DocumentView(
) {
const { document, style, ...context } = props;
return <Blocks nodes={document.nodes} blockStyle={'mt-6'} style={style} {...context} />;
return (
<Blocks
nodes={document.nodes}
ancestorBlocks={[]}
blockStyle={'mt-6'}
style={style}
{...context}
/>
);
}
+9
View File
@@ -0,0 +1,9 @@
import { DocumentBlockDrawing } from '@gitbook/api';
import { BlockProps } from './Block';
import { tcls } from '@/lib/tailwind';
export function Drawing(props: BlockProps<DocumentBlockDrawing>) {
const { style } = props;
return <div className={tcls(style)}>TODO</div>;
}
@@ -6,7 +6,7 @@ import { DocumentBlockExpandable } from '@gitbook/api';
import { Inlines } from '../Inlines';
export function Expandable(props: BlockProps<DocumentBlockExpandable>) {
const { block, style, context } = props;
const { block, style, ancestorBlocks, context } = props;
const title = getNodeFragmentByType(block, 'expandable-title');
const body = getNodeFragmentByType(block, 'expandable-body');
@@ -16,7 +16,12 @@ export function Expandable(props: BlockProps<DocumentBlockExpandable>) {
<summary className={tcls('cursor-pointer', 'px-4', 'py-3')}>
<Inlines nodes={title.nodes[0].nodes} context={context} />
</summary>
<Blocks nodes={body.nodes} context={context} style={['px-5', 'py-3']} />
<Blocks
nodes={body.nodes}
ancestorBlocks={[...ancestorBlocks, block]}
context={context}
style={['px-5', 'py-3']}
/>
</details>
);
}
+7 -2
View File
@@ -9,7 +9,7 @@ import IconCheckInCircle from '@geist-ui/icons/checkInCircle';
import { getBlockTextStyle } from './spacing';
export function Hint(props: BlockProps<DocumentBlockHint>) {
const { block, style, ...contextProps } = props;
const { block, style, ancestorBlocks, ...contextProps } = props;
const hintStyle = HINT_STYLES[block.data.style];
const firstLine = getBlockTextStyle(block.nodes[0]);
@@ -40,7 +40,12 @@ export function Hint(props: BlockProps<DocumentBlockHint>) {
>
<hintStyle.icon className={tcls('w-5', 'h-5')} />
</div>
<Blocks {...contextProps} nodes={block.nodes} style={['flex-1']} />
<Blocks
{...contextProps}
ancestorBlocks={[...ancestorBlocks, block]}
nodes={block.nodes}
style={['flex-1']}
/>
</div>
);
}
+23 -8
View File
@@ -1,18 +1,33 @@
import { tcls } from '@/lib/tailwind';
import { BlockProps } from './Block';
import { Blocks } from './Blocks';
import { DocumentBlockListItem } from '@gitbook/api';
import { DocumentBlockListItem, DocumentBlockTaskListItem } from '@gitbook/api';
export function ListItem(props: BlockProps<DocumentBlockListItem>) {
const { block, ...contextProps } = props;
export function ListItem(props: BlockProps<DocumentBlockListItem | DocumentBlockTaskListItem>) {
const { block, ancestorBlocks, ...contextProps } = props;
const isTaskList = ancestorBlocks[ancestorBlocks.length - 1]?.type === 'list-tasks';
return (
<li className={tcls('text-base', 'font-normal')}>
<Blocks
{...contextProps}
nodes={block.nodes}
blockStyle={tcls('mt-6', 'first:mt-0', 'last:mt-0')}
/>
{isTaskList ? (
<div className={tcls('flex', 'flex-row')}>
<input type="checkbox" disabled checked={block.data?.checked} />
<Blocks
{...contextProps}
nodes={block.nodes}
ancestorBlocks={[...ancestorBlocks, block]}
blockStyle={tcls('mt-6', 'first:mt-0', 'last:mt-0', 'flex-1')}
/>
</div>
) : (
<Blocks
{...contextProps}
nodes={block.nodes}
ancestorBlocks={[...ancestorBlocks, block]}
blockStyle={tcls('mt-6', 'first:mt-0', 'last:mt-0')}
/>
)}
</li>
);
}
+2 -1
View File
@@ -3,13 +3,14 @@ import { BlockProps } from './Block';
import { Blocks } from './Blocks';
export function ListOrdered(props: BlockProps<DocumentBlockListOrdered>) {
const { block, style, ...contextProps } = props;
const { block, style, ancestorBlocks, ...contextProps } = props;
return (
<Blocks
{...contextProps}
tag="ol"
nodes={block.nodes}
ancestorBlocks={[...ancestorBlocks, block]}
style={['list-decimal', 'ps-8', style]}
/>
);
+17
View File
@@ -0,0 +1,17 @@
import { DocumentBlockListTasks, DocumentBlockListUnordered } from '@gitbook/api';
import { BlockProps } from './Block';
import { Blocks } from './Blocks';
export function ListTasks(props: BlockProps<DocumentBlockListTasks>) {
const { block, style, ancestorBlocks, ...contextProps } = props;
return (
<Blocks
{...contextProps}
tag="ul"
nodes={block.nodes}
ancestorBlocks={[...ancestorBlocks, block]}
style={['list-disc', 'ps-8', style]}
/>
);
}
@@ -3,13 +3,14 @@ import { BlockProps } from './Block';
import { Blocks } from './Blocks';
export function ListUnordered(props: BlockProps<DocumentBlockListUnordered>) {
const { block, style, ...contextProps } = props;
const { block, style, ancestorBlocks, ...contextProps } = props;
return (
<Blocks
{...contextProps}
tag="ul"
nodes={block.nodes}
ancestorBlocks={[...ancestorBlocks, block]}
style={['list-disc', 'ps-8', style]}
/>
);
+2 -1
View File
@@ -3,13 +3,14 @@ import { Blocks } from './Blocks';
import { DocumentBlockQuote } from '@gitbook/api';
export function Quote(props: BlockProps<DocumentBlockQuote>) {
const { block, style, ...contextProps } = props;
const { block, style, ancestorBlocks, ...contextProps } = props;
return (
<Blocks
{...contextProps}
tag="blockquote"
nodes={block.nodes}
ancestorBlocks={[...ancestorBlocks, block]}
style={[style, 'border-l-4', 'pl-4', 'border-slate-200', 'dark:border-slate-900']}
/>
);
@@ -34,6 +34,7 @@ export async function RecordColumnValue<Tag extends React.ElementType = 'div'>(
return (
<Blocks
tag={Tag}
ancestorBlocks={[]}
nodes={fragment.nodes}
style={[columnStyle, 'w-full']}
context={context}
+9 -3
View File
@@ -5,14 +5,20 @@ import { DynamicTabs } from './DynamicTabs';
import { DocumentBlockTabs } from '@gitbook/api';
export function Tabs(props: BlockProps<DocumentBlockTabs>) {
const { block, style, context } = props;
const { block, ancestorBlocks, style, context } = props;
return (
<DynamicTabs
tabs={block.nodes.map((tab, index) => ({
id: tab.key!,
title: tab.data.title,
children: <Blocks nodes={tab.nodes} context={context} />,
title: tab.data.title ?? '',
children: (
<Blocks
nodes={tab.nodes}
ancestorBlocks={[...ancestorBlocks, block, tab]}
context={context}
/>
),
}))}
style={style}
/>