Skip to content

Commit 1b67b03

Browse files
fix: Updates for demos in docs (#295)
* Updated various documentation pages * Fixed block type dropdown bug
1 parent 1c32707 commit 1b67b03

File tree

9 files changed

+161
-121
lines changed

9 files changed

+161
-121
lines changed
Lines changed: 94 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
import { useMemo, useState } from "react";
12
import {
3+
Block,
24
BlockNoteEditor,
35
BlockSchema,
4-
DefaultBlockSchema,
6+
PartialBlock,
57
} from "@blocknote/core";
6-
import { useState } from "react";
78
import { IconType } from "react-icons";
89
import {
910
RiH1,
@@ -13,7 +14,11 @@ import {
1314
RiListUnordered,
1415
RiText,
1516
} from "react-icons/ri";
16-
import { ToolbarDropdown } from "../../../SharedComponents/Toolbar/components/ToolbarDropdown";
17+
18+
import {
19+
ToolbarDropdown,
20+
ToolbarDropdownProps,
21+
} from "../../../SharedComponents/Toolbar/components/ToolbarDropdown";
1722
import { useEditorSelectionChange } from "../../../hooks/useEditorSelectionChange";
1823
import { useEditorContentChange } from "../../../hooks/useEditorContentChange";
1924

@@ -25,13 +30,20 @@ const headingIcons: Record<HeadingLevels, IconType> = {
2530
"3": RiH3,
2631
};
2732

28-
const shouldShow = (schema: BlockSchema) => {
29-
const paragraph = "paragraph" in schema;
30-
const heading = "heading" in schema && "level" in schema.heading.propSchema;
31-
const bulletListItem = "bulletListItem" in schema;
32-
const numberedListItem = "numberedListItem" in schema;
33+
const shouldShow = <BSchema extends BlockSchema>(block: Block<BSchema>) => {
34+
if (block.type === "paragraph") {
35+
return true;
36+
}
37+
38+
if (block.type === "heading" && "level" in block.props) {
39+
return true;
40+
}
41+
42+
if (block.type === "bulletListItem") {
43+
return true;
44+
}
3345

34-
return paragraph && heading && bulletListItem && numberedListItem;
46+
return block.type === "numberedListItem";
3547
};
3648

3749
export const BlockTypeDropdown = <BSchema extends BlockSchema>(props: {
@@ -41,6 +53,77 @@ export const BlockTypeDropdown = <BSchema extends BlockSchema>(props: {
4153
props.editor.getTextCursorPosition().block
4254
);
4355

56+
const dropdownItems: ToolbarDropdownProps["items"] = useMemo(() => {
57+
const items: ToolbarDropdownProps["items"] = [];
58+
59+
if ("paragraph" in props.editor.schema) {
60+
items.push({
61+
onClick: () => {
62+
props.editor.focus();
63+
props.editor.updateBlock(block, {
64+
type: "paragraph",
65+
props: {},
66+
});
67+
},
68+
text: "Paragraph",
69+
icon: RiText,
70+
isSelected: block.type === "paragraph",
71+
});
72+
}
73+
74+
if (
75+
"heading" in props.editor.schema &&
76+
"level" in props.editor.schema.heading.propSchema
77+
) {
78+
items.push(
79+
...(["1", "2", "3"] as const).map((level) => ({
80+
onClick: () => {
81+
props.editor.focus();
82+
props.editor.updateBlock(block, {
83+
type: "heading",
84+
props: { level: level },
85+
} as PartialBlock<BSchema>);
86+
},
87+
text: "Heading " + level,
88+
icon: headingIcons[level],
89+
isSelected: block.type === "heading" && block.props.level === level,
90+
}))
91+
);
92+
}
93+
94+
if ("bulletListItem" in props.editor.schema) {
95+
items.push({
96+
onClick: () => {
97+
props.editor.focus();
98+
props.editor.updateBlock(block, {
99+
type: "bulletListItem",
100+
props: {},
101+
});
102+
},
103+
text: "Bullet List",
104+
icon: RiListUnordered,
105+
isSelected: block.type === "bulletListItem",
106+
});
107+
}
108+
109+
if ("numberedListItem" in props.editor.schema) {
110+
items.push({
111+
onClick: () => {
112+
props.editor.focus();
113+
props.editor.updateBlock(block, {
114+
type: "numberedListItem",
115+
props: {},
116+
});
117+
},
118+
text: "Numbered List",
119+
icon: RiListOrdered,
120+
isSelected: block.type === "numberedListItem",
121+
});
122+
}
123+
124+
return items;
125+
}, [block, props.editor]);
126+
44127
useEditorContentChange(props.editor, () => {
45128
setBlock(props.editor.getTextCursorPosition().block);
46129
});
@@ -49,70 +132,9 @@ export const BlockTypeDropdown = <BSchema extends BlockSchema>(props: {
49132
setBlock(props.editor.getTextCursorPosition().block);
50133
});
51134

52-
if (!shouldShow(props.editor.schema)) {
135+
if (!shouldShow(block)) {
53136
return null;
54137
}
55138

56-
// let's cast the editor because "shouldShow" has given us the confidence
57-
// the default block schema is being used
58-
let editor = props.editor as any as BlockNoteEditor<DefaultBlockSchema>;
59-
60-
const headingItems = editor.schema.heading.propSchema.level.values.map(
61-
(level) => ({
62-
onClick: () => {
63-
editor.focus();
64-
editor.updateBlock(block, {
65-
type: "heading",
66-
props: { level: level },
67-
});
68-
},
69-
text: "Heading " + level,
70-
icon: headingIcons[level],
71-
isSelected: block.type === "heading" && block.props.level === level,
72-
})
73-
);
74-
75-
return (
76-
<ToolbarDropdown
77-
items={[
78-
{
79-
onClick: () => {
80-
props.editor.focus();
81-
props.editor.updateBlock(block, {
82-
type: "paragraph",
83-
props: {},
84-
});
85-
},
86-
text: "Paragraph",
87-
icon: RiText,
88-
isSelected: block.type === "paragraph",
89-
},
90-
...headingItems,
91-
{
92-
onClick: () => {
93-
props.editor.focus();
94-
props.editor.updateBlock(block, {
95-
type: "bulletListItem",
96-
props: {},
97-
});
98-
},
99-
text: "Bullet List",
100-
icon: RiListUnordered,
101-
isSelected: block.type === "bulletListItem",
102-
},
103-
{
104-
onClick: () => {
105-
props.editor.focus();
106-
props.editor.updateBlock(block, {
107-
type: "numberedListItem",
108-
props: {},
109-
});
110-
},
111-
text: "Numbered List",
112-
icon: RiListOrdered,
113-
isSelected: block.type === "numberedListItem",
114-
},
115-
]}
116-
/>
117-
);
139+
return <ToolbarDropdown items={dropdownItems} />;
118140
};

packages/website/docs/docs/block-types.md

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ In addition to the default block types that BlockNote offers, you can also make
123123
```typescript-vue /App.tsx
124124
import {
125125
BlockNoteEditor,
126+
BlockSchema,
127+
DefaultBlockSchema,
126128
defaultBlockSchema,
127129
defaultProps,
128130
} from "@blocknote/core";
@@ -132,7 +134,7 @@ import {
132134
createReactBlockSpec,
133135
InlineContent,
134136
ReactSlashMenuItem,
135-
defaultReactSlashMenuItems,
137+
getDefaultReactSlashMenuItems,
136138
} from "@blocknote/react";
137139
import "@blocknote/core/style.css";
138140
import { RiImage2Fill } from "react-icons/ri";
@@ -146,57 +148,68 @@ export default function App() {
146148
src: {
147149
default: "https://via.placeholder.com/1000",
148150
},
151+
alt: {
152+
default: "image",
153+
},
149154
},
150155
containsInlineContent: true,
151156
render: ({ block }) => (
152157
<div id="image-wrapper">
153158
<img
154159
src={block.props.src}
155-
alt={"Image"}
160+
alt={block.props.alt}
156161
contentEditable={false}
157162
/>
158163
<InlineContent />
159164
</div>
160165
),
161166
});
162167

168+
// The custom schema, which includes the default blocks and the custom image
169+
// block.
170+
const customSchema = {
171+
// Adds all default blocks.
172+
...defaultBlockSchema,
173+
// Adds the custom image block.
174+
image: ImageBlock,
175+
} satisfies BlockSchema;
176+
163177
// Creates a slash menu item for inserting an image block.
164-
const insertImage = new ReactSlashMenuItem<
165-
DefaultBlockSchema & { image: typeof ImageBlock }
166-
>(
167-
"Insert Image",
168-
(editor) => {
178+
const insertImage: ReactSlashMenuItem<typeof customSchema> = {
179+
name: "Insert Image",
180+
execute: (editor) => {
169181
const src: string | null = prompt("Enter image URL");
182+
const alt: string | null = prompt("Enter image alt text");
183+
170184
editor.insertBlocks(
171185
[
172186
{
173187
type: "image",
174188
props: {
175189
src: src || "https://via.placeholder.com/1000",
190+
alt: alt || "image",
176191
},
177192
},
178193
],
179194
editor.getTextCursorPosition().block,
180195
"after"
181196
);
182197
},
183-
["image", "img", "picture", "media"],
184-
"Media",
185-
<RiImage2Fill />,
186-
"Insert an image"
187-
);
198+
aliases: ["image", "img", "picture", "media"],
199+
group: "Media",
200+
icon: <RiImage2Fill />,
201+
hint: "Insert an image",
202+
};
188203

189204
// Creates a new editor instance.
190205
const editor = useBlockNote({
191206
theme: "{{ getTheme(isDark) }}",
192207
// Tells BlockNote which blocks to use.
193-
blockSchema: {
194-
// Adds all default blocks.
195-
...defaultBlockSchema,
196-
// Adds the custom image block.
197-
image: ImageBlock,
198-
},
199-
slashCommands: [...defaultReactSlashMenuItems, insertImage],
208+
blockSchema: customSchema,
209+
slashMenuItems: [
210+
...getDefaultReactSlashMenuItems(customSchema),
211+
insertImage,
212+
],
200213
});
201214

202215
// Renders the editor instance using a React component.
@@ -213,7 +226,7 @@ export default function App() {
213226
}
214227

215228
img {
216-
width: "100%";
229+
width: 100%;
217230
}
218231
```
219232

packages/website/docs/docs/blocks.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,10 @@ export default function App() {
8686
const [blocks, setBlocks] = useState<Block[] | null>(null);
8787

8888
// Creates a new editor instance.
89-
const editor: BlockNoteEditor | null = useBlockNote({
89+
const editor: BlockNoteEditor = useBlockNote({
9090
theme: "{{ getTheme(isDark) }}",
9191
// Listens for when the editor's contents change.
92-
onEditorContentChange: (editor: BlockNoteEditor) =>
92+
onEditorContentChange: (editor) =>
9393
// Converts the editor's contents to an array of Block objects.
9494
setBlocks(editor.topLevelBlocks)
9595
})

packages/website/docs/docs/converting-blocks.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,10 @@ export default function App() {
6161
const [markdown, setMarkdown] = useState<string>("");
6262

6363
// Creates a new editor instance.
64-
const editor: BlockNoteEditor | null = useBlockNote({
64+
const editor: BlockNoteEditor = useBlockNote({
6565
theme: "{{ getTheme(isDark) }}",
6666
// Listens for when the editor's contents change.
67-
onEditorContentChange: (editor: BlockNoteEditor) => {
67+
onEditorContentChange: (editor) => {
6868
// Converts the editor's contents from Block objects to Markdown and
6969
// saves them.
7070
const saveBlocksAsMarkdown = async () => {
@@ -132,7 +132,7 @@ export default function App() {
132132
const [markdown, setMarkdown] = useState<string>("");
133133

134134
// Creates a new editor instance.
135-
const editor: BlockNoteEditor | null = useBlockNote({
135+
const editor: BlockNoteEditor = useBlockNote({
136136
theme: "{{ getTheme(isDark) }}",
137137
// Makes the editor non-editable.
138138
editable: false
@@ -169,7 +169,7 @@ export default function App() {
169169

170170
textarea {
171171
color: gray;
172-
background-color: #151515;
172+
background-color: {{ isDark ? "#151515" : "white" }};
173173
width: 100%;
174174
height: 100%;
175175
}
@@ -216,10 +216,10 @@ export default function App() {
216216
const [html, setHTML] = useState<string>("");
217217

218218
// Creates a new editor instance.
219-
const editor: BlockNoteEditor | null = useBlockNote({
219+
const editor: BlockNoteEditor = useBlockNote({
220220
theme: "{{ getTheme(isDark) }}",
221221
// Listens for when the editor's contents change.
222-
onEditorContentChange: (editor: BlockNoteEditor) => {
222+
onEditorContentChange: (editor) => {
223223
// Converts the editor's contents from Block objects to HTML and saves
224224
// them.
225225
const saveBlocksAsHTML = async () => {
@@ -286,7 +286,7 @@ export default function App() {
286286
const [html, setHTML] = useState<string>("");
287287

288288
// Creates a new editor instance.
289-
const editor: BlockNoteEditor | null = useBlockNote({
289+
const editor: BlockNoteEditor = useBlockNote({
290290
theme: "{{ getTheme(isDark) }}",
291291
// Makes the editor non-editable.
292292
editable: false
@@ -323,7 +323,7 @@ export default function App() {
323323

324324
textarea {
325325
color: gray;
326-
background-color: #151515;
326+
background-color: {{ isDark ? "#151515" : "white" }};
327327
width: 100%;
328328
height: 100%;
329329
}

0 commit comments

Comments
 (0)