Skip to content

Commit c0ee60e

Browse files
RND-6843: columns block (#3257)
Co-authored-by: Steven Hall <[email protected]>
1 parent f58b904 commit c0ee60e

File tree

5 files changed

+94
-6
lines changed

5 files changed

+94
-6
lines changed

.changeset/flat-wolves-poke.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"gitbook-v2": patch
3+
"gitbook": patch
4+
---
5+
6+
Adds Columns layout block to GBO

packages/gitbook/e2e/internal.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,7 @@ const testCases: TestsCase[] = [
666666
name: 'Stepper',
667667
url: 'blocks/stepper',
668668
},
669+
{ name: 'Columns', url: 'blocks/columns' },
669670
],
670671
},
671672
{

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type { ClassValue } from '@/lib/tailwind';
1212

1313
import { BlockContentRef } from './BlockContentRef';
1414
import { CodeBlock } from './CodeBlock';
15+
import { Columns } from './Columns';
1516
import { Divider } from './Divider';
1617
import type { DocumentContextProps } from './DocumentView';
1718
import { Drawing } from './Drawing';
@@ -68,6 +69,8 @@ export function Block<T extends DocumentBlock>(props: BlockProps<T>) {
6869
return <List {...props} block={block} />;
6970
case 'list-item':
7071
return <ListItem {...props} block={block} />;
72+
case 'columns':
73+
return <Columns {...props} block={block} />;
7174
case 'code':
7275
return <CodeBlock {...props} block={block} />;
7376
case 'hint':
@@ -112,10 +115,8 @@ export function Block<T extends DocumentBlock>(props: BlockProps<T>) {
112115
case 'image':
113116
case 'code-line':
114117
case 'tabs-item':
115-
throw new Error(`Blocks (${block.type}) should be directly rendered by parent`);
116-
case 'columns':
117118
case 'column':
118-
return null;
119+
throw new Error(`Blocks (${block.type}) should be directly rendered by parent`);
119120
default:
120121
return nullIfNever(block);
121122
}
@@ -171,6 +172,7 @@ export function BlockSkeleton(props: { block: DocumentBlock; style: ClassValue }
171172
case 'integration':
172173
case 'stepper':
173174
case 'reusable-content':
175+
case 'columns':
174176
return <SkeletonCard id={id} style={style} />;
175177
case 'embed':
176178
case 'images':
@@ -179,10 +181,8 @@ export function BlockSkeleton(props: { block: DocumentBlock; style: ClassValue }
179181
case 'image':
180182
case 'code-line':
181183
case 'tabs-item':
182-
throw new Error(`Blocks (${block.type}) should be directly rendered by parent`);
183-
case 'columns':
184184
case 'column':
185-
return null;
185+
throw new Error(`Blocks (${block.type}) should be directly rendered by parent`);
186186
default:
187187
return nullIfNever(block);
188188
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { type ClassValue, tcls } from '@/lib/tailwind';
2+
import type { DocumentBlockColumns, Length } from '@gitbook/api';
3+
import type { BlockProps } from '../Block';
4+
import { Blocks } from '../Blocks';
5+
6+
export function Columns(props: BlockProps<DocumentBlockColumns>) {
7+
const { block, style, ancestorBlocks, document, context } = props;
8+
return (
9+
<div className={tcls('flex flex-col gap-x-8 md:flex-row', style)}>
10+
{block.nodes.map((columnBlock) => {
11+
const width = columnBlock.data.width;
12+
const { className, style } = transformLengthToCSS(width);
13+
return (
14+
<Column key={columnBlock.key} className={className} style={style}>
15+
<Blocks
16+
key={columnBlock.key}
17+
nodes={columnBlock.nodes}
18+
document={document}
19+
ancestorBlocks={[...ancestorBlocks, block, columnBlock]}
20+
context={context}
21+
blockStyle="flip-heading-hash"
22+
style="w-full space-y-4"
23+
/>
24+
</Column>
25+
);
26+
})}
27+
</div>
28+
);
29+
}
30+
31+
export function Column(props: {
32+
children?: React.ReactNode;
33+
className?: ClassValue;
34+
style?: React.CSSProperties;
35+
}) {
36+
return (
37+
<div className={tcls('flex-col', props.className)} style={props.style}>
38+
{props.children}
39+
</div>
40+
);
41+
}
42+
43+
function transformLengthToCSS(length: Length | undefined) {
44+
if (!length) {
45+
return { className: ['md:w-full'] }; // default to full width if no length is specified
46+
}
47+
48+
if (typeof length === 'number') {
49+
return { style: undefined }; // not implemented yet with non-percentage lengths
50+
}
51+
52+
if (length.unit === '%') {
53+
return {
54+
className: [
55+
'md:flex-shrink-0',
56+
COLUMN_WIDTHS[Math.round(length.value * 0.01 * (COLUMN_WIDTHS.length - 1))],
57+
],
58+
};
59+
}
60+
61+
return { style: undefined }; // not implemented yet with non-percentage lengths
62+
}
63+
64+
// Tailwind CSS classes for column widths.
65+
// The index of the array corresponds to the percentage width of the column.
66+
const COLUMN_WIDTHS = [
67+
'md:w-0',
68+
'md:w-1/12',
69+
'md:w-2/12',
70+
'md:w-3/12',
71+
'md:w-4/12',
72+
'md:w-5/12',
73+
'md:w-6/12',
74+
'md:w-7/12',
75+
'md:w-8/12',
76+
'md:w-9/12',
77+
'md:w-10/12',
78+
'md:w-11/12',
79+
'md:w-full',
80+
];
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './Columns';

0 commit comments

Comments
 (0)