From 1a888087bca3303c604357edd6b7284204ec7945 Mon Sep 17 00:00:00 2001 From: Matthew Lipski Date: Sat, 24 Feb 2024 13:29:28 +0100 Subject: [PATCH 1/9] Added proposals for editor basics docs changes --- docs/pages/docs/_meta.json | 2 +- docs/pages/docs/editor-api/_meta.json | 5 +- docs/pages/docs/editor-api/block-content.mdx | 179 -------------- docs/pages/docs/editor-api/blocks.mdx | 53 ---- .../manipulating-inline-content.mdx | 232 ++++++++++++++++++ docs/pages/docs/editor-basics/_meta.json | 7 + docs/pages/docs/editor-basics/blocks.mdx | 53 ++++ .../default-blocks.mdx | 10 +- .../editor-basics/default-inline-content.mdx | 73 ++++++ .../{editor-api => editor-basics}/editor.mdx | 64 +++-- .../docs/editor-basics/inline-content.mdx | 74 ++++++ 11 files changed, 491 insertions(+), 261 deletions(-) delete mode 100644 docs/pages/docs/editor-api/block-content.mdx delete mode 100644 docs/pages/docs/editor-api/blocks.mdx create mode 100644 docs/pages/docs/editor-api/manipulating-inline-content.mdx create mode 100644 docs/pages/docs/editor-basics/_meta.json create mode 100644 docs/pages/docs/editor-basics/blocks.mdx rename docs/pages/docs/{editor-api => editor-basics}/default-blocks.mdx (87%) create mode 100644 docs/pages/docs/editor-basics/default-inline-content.mdx rename docs/pages/docs/{editor-api => editor-basics}/editor.mdx (54%) create mode 100644 docs/pages/docs/editor-basics/inline-content.mdx diff --git a/docs/pages/docs/_meta.json b/docs/pages/docs/_meta.json index bfd05ce72a..9d5049f9ce 100644 --- a/docs/pages/docs/_meta.json +++ b/docs/pages/docs/_meta.json @@ -1,7 +1,7 @@ { - "index": "Introduction", "quickstart": "Quickstart", + "editor-basics": "Editor Basics", "editor-api": "Editor API", "styling-theming": "Styling & Theming", "ui-components": "UI Components", diff --git a/docs/pages/docs/editor-api/_meta.json b/docs/pages/docs/editor-api/_meta.json index 325393d7bb..7d69575259 100644 --- a/docs/pages/docs/editor-api/_meta.json +++ b/docs/pages/docs/editor-api/_meta.json @@ -1,9 +1,6 @@ { - "editor":"", - "blocks": "", - "default-blocks":"", "manipulating-blocks":"", - "block-content":"", + "manipulating-inline-content":"", "cursor-selections":"", "converting-blocks":"" } diff --git a/docs/pages/docs/editor-api/block-content.mdx b/docs/pages/docs/editor-api/block-content.mdx deleted file mode 100644 index 45fa95ade0..0000000000 --- a/docs/pages/docs/editor-api/block-content.mdx +++ /dev/null @@ -1,179 +0,0 @@ ---- -title: Block Content -description: TODO -imageTitle: Block Content -path: /docs/block-content ---- - -TODO: where should this page be? and / or do we need API changes? (matthew has some thoughts on this) - -# Block Content - -The `content` field of a `Block` describes the content of the block. For all blocks except tables, this field is an array of `InlineContent` objects and describes the rich text content typed inside the block. - -In the example below, you can see how a sentence like "Hello __there__, [BlockNote](https://www.blocknotejs.org)" is represented as an array of `InlineContent` objects: - -TODO: DEMO - - -## `InlineContent` Types - -`InlineContent` can be styled text (rich text), a link, or a mention: - -### Styled Text - -`StyledText` is a type of `InlineContent` used to display pieces of text with styles: - -```typescript -type StyledText = { - type: "text"; - text: string; - styles: Styles; -}; -``` - -`text:` The displayed text. - -`styles:` The styles that are applied to the text. - -**Styles Object** - -`StyledText` supports a variety of styles, including bold, underline, and text color, which are represented using a `Styles` object: - -```typescript -type Styles = Partial<{ - bold: true; - italic: true; - underline: true; - strikethrough: true; - textColor: string; - backgroundColor: string; -}>; -``` - -### Links - -`Link` objects represent links to a URL: - -```typescript -type Link = { - type: "link"; - content: StyledText[]; - href: string; -}; -``` - -`content:` The styled text used to display the link. - -`href:` The URL that opens when clicking the link. - -### Mentions - -TODO - -### Custom `InlineContent` and `Styles` - -You can create your own types of `InlineContent` and `Styles` using React. Skip to the Advanced "custom schemas" section to learn more about extending the editor. (TODO: link) - -## TableContent - -Most blocks use an array of `InlineContent` objects to describe their content, but tables are slightly different. Table blocks have `TableContent` as their content, in which each row contains an array of cells, that - - -```typescript -type TableContent = { - type: "tableContent"; - rows: { - cells: InlineContent[][]; - }[]; -}; -``` - -## Editor Functions - -TODO: where should this be? - -While `InlineContent` objects are used to describe a block's content, they can be cumbersome to work with directly. Therefore, BlockNote exposes functions which make it easier to edit block contents. - -### Accessing Styles - -You can get the styles at the current [Text Cursor Position](/docs/cursor-selections#text-cursor) using the following function: - -```typescript -getActiveStyles(): Styles; - -// Usage -const styles = editor.getActiveStyles(); -``` - -If a [Selection](/docs/cursor-selections#selections) is active, this function returns the active styles at the end of the selection. - -### Adding Styles - -You can add styles to the currently selected text using the following function: - -```typescript -addStyles(styles: Styles): void; - -// Usage -editor.addStyles({ textColor: "red" }); -``` - -### Removing Styles - -You can remove styles from the currently selected text using the following function: - -```typescript -removeStyles(styles: Styles): void; - -// Usage -editor.removeStyles({ bold: true }); -``` - -### Toggling Styles - -You can toggle styles on the currently selected text using the following function: - -```typescript -toggleStyles(styles: Styles): void; - -// Usage -editor.toggleStyles({ bold: true, italic: true }); -``` - -### Accessing Selected Text - -You can get the currently selected text using the following function: - -```typescript -getSelectedText(): string; - -// Usage -const text = editor.getSelectedText(); -``` - -### Accessing Selected Link - -You can get the URL of the link in the current selection the following function: - -```typescript -getSelectedLink(): string | undefined; - -// Usage -const linkUrl = editor.getSelectedLink(); -``` - -If there are multiple links in the selection, this function only returns the last one's URL. If there are no links, returns `undefined`. - -### Creating a Link - -You can create a new link using the following function: - -```typescript -createLink(url: string, text?: string): void; - -// Usage -editor.createLink("https://www.blocknotejs.org/", "BlockNote"); -``` - -If a [Selection](/docs/cursor-selections#selections) is active, the new link will replace the currently selected content. diff --git a/docs/pages/docs/editor-api/blocks.mdx b/docs/pages/docs/editor-api/blocks.mdx deleted file mode 100644 index 133e435af3..0000000000 --- a/docs/pages/docs/editor-api/blocks.mdx +++ /dev/null @@ -1,53 +0,0 @@ ---- -title: Introduction to Blocks -description: So, you've set up a BlockNote editor and your users can start writing content, organized in blocks. What are blocks exactly, and how do we access the blocks from code? -imageTitle: Introduction to Blocks ---- - -import { Example } from "@/components/example"; - -[ ] Maybe move outside of "Editor API" or create separate "Editor basics" page? - -# Introduction to Blocks - -So, you've set up a BlockNote editor and your users can start writing content, organized in blocks. What are blocks exactly, and how do we access the blocks from code? - -## Editor Functions - -The `editor` returned from `useCreateBlockNote` exposes functions to access and update blocks. -We'll go through the full API later, but let's start with a simple example that shows the document (a list of blocks) in JSON. -Play around with the editor to see how blocks are represented in JSON. - - - -## Block Objects - -So, BlockNote is centered around the idea of blocks. A block - like a heading, paragraph, or list item - contains a piece of content and optionally nested blocks: - -image - -In code, the `Block` type is used to describe any given block in the editor: - -```typescript -type Block = { - id: string; - type: string; // the block type, e.g. 'paragraph', 'heading' - props: Record; // exact props depends on the Block type - content: InlineContent[] | TableContent; - children: Block[]; -}; -``` - -`id:` The block's ID. Multiple blocks cannot share a single ID, and a block will keep the same ID from when it's created until it's removed. - -`type:` The block's type, such as a paragraph, heading, or list item. For an overview of built-in block types, see [Built-In Block Types](/docs/block-types#built-in-block-types). - -`props:` The block's properties, which are stored in a set of key/value pairs and specify how the block looks and behaves. Different block types have different props - see [Block Types & Properties](/docs/block-types) for more. - -`content:` The block's rich text content, represented as an array of `InlineContent` objects. This does not include content from any nested blocks. [Table](/docs/block-types#table) blocks are slightly different, as they contain `TableContent`, where each table cell is represented as an array of `InlineContent` objects. For more information on `InlineContent` and `TableContent` objects, visit [Inline Content](/docs/inline-content). - -`children:` Any blocks nested inside the block. The nested blocks are also represented using `Block` objects. diff --git a/docs/pages/docs/editor-api/manipulating-inline-content.mdx b/docs/pages/docs/editor-api/manipulating-inline-content.mdx new file mode 100644 index 0000000000..738d5754b5 --- /dev/null +++ b/docs/pages/docs/editor-api/manipulating-inline-content.mdx @@ -0,0 +1,232 @@ +--- +title: Manipulating Inline Content +description: TODO +imageTitle: Manipulating Inline Content +path: /docs/block-content +--- + +TODO: API For this is pretty weird vs blocks + +[//]: # (# Block Content) + +[//]: # () +[//]: # (The `content` field of a `Block` describes the content of the block. For all blocks except tables, this field is an array of `InlineContent` objects and describes the rich text content typed inside the block.) + +[//]: # () +[//]: # (In the example below, you can see how a sentence like "Hello __there__, [BlockNote](https://www.blocknotejs.org)" is represented as an array of `InlineContent` objects:) + +[//]: # () +[//]: # (TODO: DEMO) + +[//]: # () +[//]: # () +[//]: # (## `InlineContent` Types) + +[//]: # () +[//]: # (`InlineContent` can be styled text (rich text), a link, or a mention:) + +[//]: # () +[//]: # (### Styled Text) + +[//]: # () +[//]: # (`StyledText` is a type of `InlineContent` used to display pieces of text with styles:) + +[//]: # () +[//]: # (```typescript) + +[//]: # (type StyledText = {) + +[//]: # ( type: "text";) + +[//]: # ( text: string;) + +[//]: # ( styles: Styles;) + +[//]: # (};) + +[//]: # (```) + +[//]: # () +[//]: # (`text:` The displayed text.) + +[//]: # () +[//]: # (`styles:` The styles that are applied to the text.) + +[//]: # () +[//]: # (**Styles Object**) + +[//]: # () +[//]: # (`StyledText` supports a variety of styles, including bold, underline, and text color, which are represented using a `Styles` object:) + +[//]: # () +[//]: # (```typescript) + +[//]: # (type Styles = Partial<{) + +[//]: # ( bold: true;) + +[//]: # ( italic: true;) + +[//]: # ( underline: true;) + +[//]: # ( strikethrough: true;) + +[//]: # ( textColor: string;) + +[//]: # ( backgroundColor: string;) + +[//]: # (}>;) + +[//]: # (```) + +[//]: # () +[//]: # (### Links) + +[//]: # () +[//]: # (`Link` objects represent links to a URL:) + +[//]: # () +[//]: # (```typescript) + +[//]: # (type Link = {) + +[//]: # ( type: "link";) + +[//]: # ( content: StyledText[];) + +[//]: # ( href: string;) + +[//]: # (};) + +[//]: # (```) + +[//]: # () +[//]: # (`content:` The styled text used to display the link.) + +[//]: # () +[//]: # (`href:` The URL that opens when clicking the link.) + +[//]: # () +[//]: # (### Mentions) + +[//]: # () +[//]: # (TODO) + +[//]: # () +[//]: # (### Custom `InlineContent` and `Styles`) + +[//]: # () +[//]: # (You can create your own types of `InlineContent` and `Styles` using React. Skip to the Advanced "custom schemas" section to learn more about extending the editor. (TODO: link)) + +[//]: # () +[//]: # (## TableContent) + +[//]: # () +[//]: # (Most blocks use an array of `InlineContent` objects to describe their content, but tables are slightly different. Table blocks have `TableContent` as their content, in which each row contains an array of cells, that) + +[//]: # () +[//]: # () +[//]: # (```typescript) + +[//]: # (type TableContent = {) + +[//]: # ( type: "tableContent";) + +[//]: # ( rows: {) + +[//]: # ( cells: InlineContent[][];) + +[//]: # ( }[];) + +[//]: # (};) + +[//]: # (```) + +# Manipulating Inline Content + +TODO: where should this be? + +While `InlineContent` objects are used to describe a block's content, they can be cumbersome to work with directly. Therefore, BlockNote exposes functions which make it easier to edit block contents. + +## Accessing Styles + +You can get the styles at the current [Text Cursor Position](/docs/cursor-selections#text-cursor) using the following function: + +```typescript +getActiveStyles(): Styles; + +// Usage +const styles = editor.getActiveStyles(); +``` + +If a [Selection](/docs/cursor-selections#selections) is active, this function returns the active styles at the end of the selection. + +## Adding Styles + +You can add styles to the currently selected text using the following function: + +```typescript +addStyles(styles: Styles): void; + +// Usage +editor.addStyles({ textColor: "red" }); +``` + +## Removing Styles + +You can remove styles from the currently selected text using the following function: + +```typescript +removeStyles(styles: Styles): void; + +// Usage +editor.removeStyles({ bold: true }); +``` + +## Toggling Styles + +You can toggle styles on the currently selected text using the following function: + +```typescript +toggleStyles(styles: Styles): void; + +// Usage +editor.toggleStyles({ bold: true, italic: true }); +``` + +## Accessing Selected Text + +You can get the currently selected text using the following function: + +```typescript +getSelectedText(): string; + +// Usage +const text = editor.getSelectedText(); +``` + +## Accessing Selected Link + +You can get the URL of the link in the current selection the following function: + +```typescript +getSelectedLink(): string | undefined; + +// Usage +const linkUrl = editor.getSelectedLink(); +``` + +If there are multiple links in the selection, this function only returns the last one's URL. If there are no links, returns `undefined`. + +## Creating a Link + +You can create a new link using the following function: + +```typescript +createLink(url: string, text?: string): void; + +// Usage +editor.createLink("https://www.blocknotejs.org/", "BlockNote"); +``` + +If a [Selection](/docs/cursor-selections#selections) is active, the new link will replace the currently selected content. diff --git a/docs/pages/docs/editor-basics/_meta.json b/docs/pages/docs/editor-basics/_meta.json new file mode 100644 index 0000000000..1b07a43c0b --- /dev/null +++ b/docs/pages/docs/editor-basics/_meta.json @@ -0,0 +1,7 @@ +{ + "editor": "Editor Setup", + "blocks": "Intro to Blocks", + "inline-content": "Intro to Inline Content", + "default-blocks": "Default Blocks", + "default-inline-content": "Default Inline Content" +} diff --git a/docs/pages/docs/editor-basics/blocks.mdx b/docs/pages/docs/editor-basics/blocks.mdx new file mode 100644 index 0000000000..1d0956a6e8 --- /dev/null +++ b/docs/pages/docs/editor-basics/blocks.mdx @@ -0,0 +1,53 @@ +--- +title: Introduction to Blocks +description: Once you create an editor, you'll need to understand how its content is structured into blocks. +imageTitle: Introduction to Blocks +--- + +import { Example } from "@/components/example"; + +# Intro to Blocks + +Each BlockNote editor is made up of a list of blocks. This page explains what blocks are, and how they're represented in code. + +## Block Objects + +A block - like a heading, paragraph, or list item - contains a piece of content and optionally nested blocks: + +image + +In code, the `Block` type is used to describe any given block in the editor: + +```typescript +type Block = { + id: string; + type: string; // the block type, e.g. 'paragraph', 'heading' + props: Record; // exact props depends on the Block type + content: InlineContent[] | TableContent | undefined; + children: Block[]; +}; +``` + +`id:` The block's ID. Multiple blocks cannot share a single ID, and a block will keep the same ID from when it's created until it's removed. + +`type:` The block's type, such as a paragraph, heading, or list item. For an overview of built-in block types, see [Default Blocks](/docs/editor-basics/default-blocks). + +`props:` The block's properties, which are stored in a set of key/value pairs and specify how the block looks and behaves. Different block types have different props - see [Default Blocks](/docs/editor-basics/default-blocks) for more. + +`content:` The block's rich text content, usually represented as an array of `InlineContent` objects. This does not include content from any nested blocks. For more information visit [Intro to Inline Content](/docs/editor-api/block-content). + +[Table](/docs/editor-basics/default-blocks#table) blocks are slightly different, as they contain `TableContent`, where each table cell is represented as an array of `InlineContent` objects. Some blocks, like [Image](/docs/editor-basics/default-blocks#image) blocks, don't contain any rich text content, so their `content` is `undefined`. + +`children:` Any blocks nested inside the block. The nested blocks are also represented using `Block` objects. + +## Displaying Editor Content + +After [creating an editor](/docs/editor-basics/editor#creating-the-editor), you'll have a `BlockNoteEditor` object, which exposes various functions to read and modify the editor. + +We'll go through the full API later, but let's start with a simple example that shows the editor content - notice that it's in JSON as an array of `Block` objects. + + diff --git a/docs/pages/docs/editor-api/default-blocks.mdx b/docs/pages/docs/editor-basics/default-blocks.mdx similarity index 87% rename from docs/pages/docs/editor-api/default-blocks.mdx rename to docs/pages/docs/editor-basics/default-blocks.mdx index 024f858bca..d7b22f96a0 100644 --- a/docs/pages/docs/editor-api/default-blocks.mdx +++ b/docs/pages/docs/editor-basics/default-blocks.mdx @@ -6,9 +6,7 @@ imageTitle: Default Blocks import { Example } from "@/components/example"; -# Default blocks - -TBD: move to a separate "reference" section? +# Default Blocks BlockNote includes a number of built-in block types. The demo editor below displays all of them: @@ -142,7 +140,7 @@ type TableBlock = { ## Default Block Properties -While each type of block can have its own set of properties, there are some properties that all built-in block types have by default, which you can find in the definition for `DefaultProps`: +There are some default block props that BlockNote uses for its default block types, and also exports for use in your own [custom blocks](/docs/custom-schemas/custom-blocks): ```typescript type DefaultProps = { @@ -158,6 +156,6 @@ type DefaultProps = { `textAlignment:` The text alignment of the block. -## Creating your own blocks +## Creating New Block Types -You can create your own custom blocks using React. Skip to the Advanced "custom schemas" section to learn more about extending the editor. (TODO: link) \ No newline at end of file +Skip to [Custom Blocks](/docs/custom-schemas/custom-blocks) to learn how to do this. \ No newline at end of file diff --git a/docs/pages/docs/editor-basics/default-inline-content.mdx b/docs/pages/docs/editor-basics/default-inline-content.mdx new file mode 100644 index 0000000000..3b7a37ca8f --- /dev/null +++ b/docs/pages/docs/editor-basics/default-inline-content.mdx @@ -0,0 +1,73 @@ +--- +title: Default Inline Content +description: Overview of all default inline content types and their properties in BlockNote. +imageTitle: Default Inline Content +--- + +# Default Inline Content + +BlockNote includes a number of built-in inline content types. The demo editor below displays all of them: + +TODO: demo + +## Reference + +Here's an overview of all default inline content and the properties they support: + +### Styled Text + +`StyledText` is a type of `InlineContent` used to display pieces of text with styles: + +```typescript +type Styles = { + bold: true; + italic: true; + underline: true; + strikethrough: true; + textColor: string; + backgroundColor: string; +} + +type StyledText = { + type: "text"; + text: string; + styles: Styles; +}; +``` + +### Links + +`Link` objects represent links to a URL: + +```typescript +type Link = { + type: "link"; + content: StyledText[]; + href: string; +}; +``` + +## Default Inline Content Properties + +There are some default inline content props that BlockNote uses for its default inline content types, and also exports for use in your own [custom inline content](/docs/custom-schemas/custom-inline-content): + +```typescript +type Styles = { + bold: true; + italic: true; + underline: true; + strikethrough: true; + textColor: string; + backgroundColor: string; +} + +type DefaultInlineContentProps = { + styles: Styles +}; +``` + +`styles:` The text styles applied to the inline content. Only useful for editable inline content types. + +## Creating New Inline Content Types + +You can create your own custom blocks using React. Skip to [Custom Inline Content](/docs/custom-schemas/custom-inline-content) to learn how to do this. \ No newline at end of file diff --git a/docs/pages/docs/editor-api/editor.mdx b/docs/pages/docs/editor-basics/editor.mdx similarity index 54% rename from docs/pages/docs/editor-api/editor.mdx rename to docs/pages/docs/editor-basics/editor.mdx index 5429872d54..5349080d0d 100644 --- a/docs/pages/docs/editor-api/editor.mdx +++ b/docs/pages/docs/editor-basics/editor.mdx @@ -1,8 +1,8 @@ --- title: Editor Setup -description: While you can get started with BlockNote in minutes, it's likely that you'll want to customize its features and functionality to better suit your app. -imageTitle: Customizing the Editor -path: /docs/editor +description: While BlockNote is ready to use out-of-the-box, there are a number of ways in which you can set it up, to better suit your use case. +imageTitle: Editor Setup +path: /docs/editor-basics/editor --- import { Example } from "@/components/example"; @@ -18,13 +18,42 @@ TODO: # Editor Setup -While you can get started with BlockNote in minutes, it's likely that you'll -want to customize its features and functionality to better suit your app. +While BlockNote is ready to use out-of-the-box, there are a number of ways in which you can set it up, to better suit your use case. -## Editor Options +## Creating the Editor -There are a number of options that you can pass to `useCreateBlockNote()`, which you -can use to customize the editor. You can find the full list of these below: +### Using a React Hook + +Most of the time, you'll want to create the BlockNote editor using the `useCreateBlockNote` hook. This will create a new editor as your React component gets mounted, and you can pass a dependency array as with other React hooks. + +```ts +useCreateBlockNote = ( + options?: Partial, + deps?: React.DependencyList +) => BlockNoteEditor; +``` + +Read on to [Options](/docs/editor-basics/editor#options) to see what options you can pass to the editor. + +### Using a Method + +You can also create a new editor using `BlockNoteEditor.create`. You should use this when you want to wait after your React component gets mounted before creating the editor. + +```ts +BlockNoteEditor.create = ( + options?: Partial, +) => BlockNoteEditor; +``` + +In the demo below, we use `BlockNoteEditor.create` so we can fetch the editor's initial content from `localStorage` before creating it. + + + +Read on to [Options](/docs/editor-basics/editor#options) to see what options you can pass to the editor. + +### Options + +There are a number of options that you can pass to `useCreateBlockNote()` and `BlockNoteEditor.create`. You can find the full list of these below: ```typescript export type BlockNoteEditorOptions = Partial<{ @@ -58,19 +87,18 @@ export type BlockNoteEditorOptions = Partial<{ `styleSpecs` (_advanced_): Specifications for Custom Styles. See [Style Specs](/docs/style-specs) for more info. -## `useCreateBlockNote` - -## `BlockNoteView` +## Rendering the Editor -- editable -- events +### Using a React Component -### Removing UI Elements +To, render the editor, you should use the `BlockNoteView` component, and pass in the editor created using `useCreateBlockNote` or `BlockNoteEditor.create`: -TODO: probably just explain the props of BlockNoteView +```tsx +const editor = useCreateBlockNote(); -In the following example, we remove the Side Menu from the editor. This is done by adding all `Positioner` components as children of `BlockNoteView`, for each UI element except the Side Menu: +return +``` - +### Props -Each further `Positioner` component you remove will remove its corresponding UI element from the editor. If you only want to keep the editor itself, add only an empty fragment (`<>`) to `BlockNoteView`'s children. +TODO diff --git a/docs/pages/docs/editor-basics/inline-content.mdx b/docs/pages/docs/editor-basics/inline-content.mdx new file mode 100644 index 0000000000..bf22efda21 --- /dev/null +++ b/docs/pages/docs/editor-basics/inline-content.mdx @@ -0,0 +1,74 @@ +--- +title: Introduction to Blocks +description: Once you create an editor, you'll need to understand how its content is structured into blocks. +imageTitle: Introduction to Blocks +--- + +import { Example } from "@/components/example"; + +# Intro to Inline Content + +Many blocks contain inline content. This page explains what inline content is, and how it's represented in code. + +## Inline Content Objects + +A block's content is referred to as inline content, and is used to represent rich text. + +TODO: Between custom & default inline content types, the shapes on inline content objects are not standardized. +e.g. `Link`s have `href` and no props, custom IC will have props. `StyledText` can be IC itself, or used in other IC types. + +I think we should have smth like this: +```typescript +// Helper types +type Styles = { + bold: boolean; + textColor: string; + ... +} + +// Inline Content types +type StyledText = { + type: "richText"; + props: { + styles: Styles; + } + editable: true; +} +type Link = { + type: "link"; + props: { + styles: Styles; + href: string; + } + editable: true; +} +type Mention = { + type: "mention"; + props: { + user: string; + } + editable: false; +} +``` +This is way cleaner, but means that links cannot have mixed styles. I think we either: +- Convert links to styles (they're already marks in TipTap) +- Not do anything bc I don't think having mixed styles on links is ever useful + +## Table Content + +Most blocks use an array of `InlineContent` objects to describe their content, but tables are slightly different. Table blocks have `TableContent` as their content, in which each row contains an array of cells, that each have an array of `InlineContent` objects. + +```typescript +type TableContent = { + type: "tableContent"; + rows: { + cells: InlineContent[][]; + }[]; +}; +``` + +## Displaying Block Content + +In the example below, you can see how a block with the content, "Hello __there__, [BlockNote](https://www.blocknotejs.org)", is represented as an array of `InlineContent` objects: + +TODO: DEMO From d6bc4347cc78a07ccddd354138fcd5d5b18c84dc Mon Sep 17 00:00:00 2001 From: Matthew Lipski Date: Mon, 26 Feb 2024 15:42:25 +0100 Subject: [PATCH 2/9] Addressed editor basics TODOs --- docs/pages/docs/editor-basics/_meta.json | 8 +- docs/pages/docs/editor-basics/blocks.mdx | 53 ------- .../docs/editor-basics/content-structure.mdx | 134 ++++++++++++++++++ ...t-blocks.mdx => default-content-types.mdx} | 112 ++++++++++++--- .../editor-basics/default-inline-content.mdx | 73 ---------- docs/pages/docs/editor-basics/editor.mdx | 52 ++++++- .../docs/editor-basics/inline-content.mdx | 74 ---------- examples/01-basic/02-block-objects/App.tsx | 20 ++- examples/01-basic/02-block-objects/README.md | 4 +- examples/01-basic/all-blocks/.bnexample.json | 4 + examples/01-basic/all-blocks/App.tsx | 53 +++++++ examples/01-basic/all-blocks/README.md | 7 + .../all-inline-content/.bnexample.json | 4 + examples/01-basic/all-inline-content/App.tsx | 45 ++++++ .../01-basic/all-inline-content/README.md | 8 ++ .../inline-content-objects/.bnexample.json | 4 + .../01-basic/inline-content-objects/App.tsx | 71 ++++++++++ .../01-basic/inline-content-objects/README.md | 7 + .../inline-content-objects/styles.css | 3 + 19 files changed, 505 insertions(+), 231 deletions(-) delete mode 100644 docs/pages/docs/editor-basics/blocks.mdx create mode 100644 docs/pages/docs/editor-basics/content-structure.mdx rename docs/pages/docs/editor-basics/{default-blocks.mdx => default-content-types.mdx} (51%) delete mode 100644 docs/pages/docs/editor-basics/default-inline-content.mdx delete mode 100644 docs/pages/docs/editor-basics/inline-content.mdx create mode 100644 examples/01-basic/all-blocks/.bnexample.json create mode 100644 examples/01-basic/all-blocks/App.tsx create mode 100644 examples/01-basic/all-blocks/README.md create mode 100644 examples/01-basic/all-inline-content/.bnexample.json create mode 100644 examples/01-basic/all-inline-content/App.tsx create mode 100644 examples/01-basic/all-inline-content/README.md create mode 100644 examples/01-basic/inline-content-objects/.bnexample.json create mode 100644 examples/01-basic/inline-content-objects/App.tsx create mode 100644 examples/01-basic/inline-content-objects/README.md create mode 100644 examples/01-basic/inline-content-objects/styles.css diff --git a/docs/pages/docs/editor-basics/_meta.json b/docs/pages/docs/editor-basics/_meta.json index 1b07a43c0b..347454b190 100644 --- a/docs/pages/docs/editor-basics/_meta.json +++ b/docs/pages/docs/editor-basics/_meta.json @@ -1,7 +1,5 @@ { - "editor": "Editor Setup", - "blocks": "Intro to Blocks", - "inline-content": "Intro to Inline Content", - "default-blocks": "Default Blocks", - "default-inline-content": "Default Inline Content" + "content-structure": "Content Structure", + "default-content-types": "Default Content Types", + "editor": "Editor Setup" } diff --git a/docs/pages/docs/editor-basics/blocks.mdx b/docs/pages/docs/editor-basics/blocks.mdx deleted file mode 100644 index 1d0956a6e8..0000000000 --- a/docs/pages/docs/editor-basics/blocks.mdx +++ /dev/null @@ -1,53 +0,0 @@ ---- -title: Introduction to Blocks -description: Once you create an editor, you'll need to understand how its content is structured into blocks. -imageTitle: Introduction to Blocks ---- - -import { Example } from "@/components/example"; - -# Intro to Blocks - -Each BlockNote editor is made up of a list of blocks. This page explains what blocks are, and how they're represented in code. - -## Block Objects - -A block - like a heading, paragraph, or list item - contains a piece of content and optionally nested blocks: - -image - -In code, the `Block` type is used to describe any given block in the editor: - -```typescript -type Block = { - id: string; - type: string; // the block type, e.g. 'paragraph', 'heading' - props: Record; // exact props depends on the Block type - content: InlineContent[] | TableContent | undefined; - children: Block[]; -}; -``` - -`id:` The block's ID. Multiple blocks cannot share a single ID, and a block will keep the same ID from when it's created until it's removed. - -`type:` The block's type, such as a paragraph, heading, or list item. For an overview of built-in block types, see [Default Blocks](/docs/editor-basics/default-blocks). - -`props:` The block's properties, which are stored in a set of key/value pairs and specify how the block looks and behaves. Different block types have different props - see [Default Blocks](/docs/editor-basics/default-blocks) for more. - -`content:` The block's rich text content, usually represented as an array of `InlineContent` objects. This does not include content from any nested blocks. For more information visit [Intro to Inline Content](/docs/editor-api/block-content). - -[Table](/docs/editor-basics/default-blocks#table) blocks are slightly different, as they contain `TableContent`, where each table cell is represented as an array of `InlineContent` objects. Some blocks, like [Image](/docs/editor-basics/default-blocks#image) blocks, don't contain any rich text content, so their `content` is `undefined`. - -`children:` Any blocks nested inside the block. The nested blocks are also represented using `Block` objects. - -## Displaying Editor Content - -After [creating an editor](/docs/editor-basics/editor#creating-the-editor), you'll have a `BlockNoteEditor` object, which exposes various functions to read and modify the editor. - -We'll go through the full API later, but let's start with a simple example that shows the editor content - notice that it's in JSON as an array of `Block` objects. - - diff --git a/docs/pages/docs/editor-basics/content-structure.mdx b/docs/pages/docs/editor-basics/content-structure.mdx new file mode 100644 index 0000000000..2b973f05c2 --- /dev/null +++ b/docs/pages/docs/editor-basics/content-structure.mdx @@ -0,0 +1,134 @@ +--- +title: Content Structure +description: If you want to make the most out of BlockNote, it's important to understand how the editor's content is structured. +imageTitle: Content Structure +--- + +import { Example } from "@/components/example"; + +# Content Structure + +If you want to make the most out of BlockNote, it's important to understand how the editor's content is structured. + +## Blocks + +Each BlockNote editor is made up of a list of blocks. A block - like a heading, paragraph, or list item - contains a piece of content and optionally nested blocks: + +image + +### Block Objects + +In code, the `Block` type is used to describe any given block in the editor: + +```typescript +type Block = { + id: string; + type: string; + props: Record; + content: InlineContent[] | TableContent | undefined; + children: Block[]; +}; +``` + +`id:` The block's ID. Multiple blocks cannot share a single ID, and a block will keep the same ID from when it's created until it's removed. + +`type:` The block's type, such as a paragraph, heading, or list item. For an overview of built-in block types, see [Default Blocks](/docs/editor-basics/default-content-types#default-blocks). + +`props:` The block's properties, which is a set of key/value pairs that further specify how the block looks and behaves. Different block types have different props - see [Default Blocks](/docs/editor-basics/default-content-types#default-blocks) for more. + +`content:` The block's rich text content, usually represented as an array of `InlineContent` objects. This does not include content from any nested blocks. Read on to [Inline Content](/docs/editor-basics/content-structure#inline-content) for more on this. + +`children:` Any blocks nested inside the block. The nested blocks are also represented using `Block` objects. + +### Editor Content in JSON + +The demo below shows how the editor content is represented in code - notice that it's in JSON as an array of `Block` objects. + + + +## Inline Content + +A block's content is referred to as inline content, and is used to represent rich text. + +TODO: Between custom & default inline content types, the shapes on inline content objects are not standardized. +e.g. `Link`s have `href` and no props, custom IC will have props. `StyledText` can be IC itself, or used in other IC types. + +I think we should have smth like this: +```typescript +// Helper types +type Styles = { + bold: boolean; + textColor: string; + ... +} + +// Inline Content types +type StyledText = { + type: "richText"; + props: { + styles: Styles; + } + editable: true; +} +type Link = { + type: "link"; + props: { + styles: Styles; + href: string; + } + editable: true; +} +type Mention = { + type: "mention"; + props: { + user: string; + } + editable: false; +} +``` +This is way cleaner, but means that links cannot have mixed styles. I think we either: +- Convert links to styles (they're already marks in TipTap) +- Not do anything bc I don't think having mixed styles on links is ever useful + +### Inline Content Objects + +In code, the `InlineContent` type is used to describe a piece of inline content: + +```typescript +type InlineContent = { + type: string; + props: Record; + editable: boolean; +}; +``` + +`type:` The inline content's type, such as a mention or styled text. For an overview of built-in inline content types, see [Default Inline Content](/docs/editor-basics/default-content-types#default-inline-content). + +`props:` The inline content's properties, which are stored in a set of key/value pairs and specify how the inline content looks and behaves. Different inline content types have different props - see [Default Inline Content](/docs/editor-basics/default-content-types#default-inline-content) for more. + +`editable`: Whether the inline content contains editable text. + +### Types of Block Content + +Most blocks will use an array of `InlineContent` objects to describe their content, such as paragraphs, headings and list items. Some blocks, like [images](/docs/editor-basics/default-content-types#image), don't contain any rich text content, so their `content` fields will be `undefined`. + +[Tables](/docs/editor-basics/default-content-types#table) are also different, as they contain `TableContent`. Here, each table cell is represented as an array of `InlineContent` objects: + +```typescript +type TableContent = { + type: "tableContent"; + rows: { + cells: InlineContent[][]; + }[]; +}; +``` + +### Block Content in JSON + +The demo below shows how the block content is represented in code by outputting only the `content` field of each top level block. As in the [previous demo](/docs/editor-basics/content-structure#editor-content-in-json), notice that it's in JSON. + + \ No newline at end of file diff --git a/docs/pages/docs/editor-basics/default-blocks.mdx b/docs/pages/docs/editor-basics/default-content-types.mdx similarity index 51% rename from docs/pages/docs/editor-basics/default-blocks.mdx rename to docs/pages/docs/editor-basics/default-content-types.mdx index d7b22f96a0..2c0fae1bb5 100644 --- a/docs/pages/docs/editor-basics/default-blocks.mdx +++ b/docs/pages/docs/editor-basics/default-content-types.mdx @@ -1,22 +1,26 @@ --- -title: Default Blocks -description: Overview of all default block types and their properties in BlockNote. -imageTitle: Default Blocks +title: Default Content Types +description: BlockNote supports a variety on built-in block and inline content types that are included in the editor by default. +imageTitle: Default Content Types --- import { Example } from "@/components/example"; -# Default Blocks +# Default Content Types -BlockNote includes a number of built-in block types. The demo editor below displays all of them: +BlockNote supports a variety on built-in block and inline content types that are included in the editor by default. To create your own content types, see [Custom Schemas](/docs/custom-schemas). -TODO: demo +## Default Blocks -## Reference +BlockNote includes a number of built-in block types. The demo below contains each of them: + + + +### Reference Here's an overview of all default blocks and the properties they support: -### Paragraph +#### Paragraph **Appearance** @@ -34,7 +38,7 @@ type ParagraphBlock = { }; ``` -### Heading +#### Heading **Appearance** @@ -56,7 +60,7 @@ type HeadingBlock = { `level:` The heading level, representing a title (`level: 1`), heading (`level: 2`), and subheading (`level: 3`). -### Bullet List Item +#### Bullet List Item **Appearance** @@ -74,7 +78,7 @@ type BulletListItemBlock = { }; ``` -### Numbered List Item +#### Numbered List Item **Appearance** @@ -92,7 +96,7 @@ type NumberedListItemBlock = { }; ``` -### Image +#### Image **Appearance** @@ -108,8 +112,8 @@ type ImageBlock = { url: string = ""; caption: string = ""; width: number = 512; - } & Omit; - content: InlineContent[]; + } & DefaultProps; + content: undefined; children: Block[]; }; ``` @@ -120,7 +124,7 @@ type ImageBlock = { `width:` The image width in pixels. -### Table +#### Table **Appearance** @@ -138,7 +142,7 @@ type TableBlock = { }; ``` -## Default Block Properties +### Default Block Properties There are some default block props that BlockNote uses for its default block types, and also exports for use in your own [custom blocks](/docs/custom-schemas/custom-blocks): @@ -156,6 +160,78 @@ type DefaultProps = { `textAlignment:` The text alignment of the block. -## Creating New Block Types +### Creating New Block Types + +Skip to [Custom Blocks](/docs/custom-schemas/custom-blocks) to learn how to do this. + +## Default Inline Content + +BlockNote includes a number of built-in inline content types. The demo editor below displays all of them: + + + +### Reference + +Here's an overview of all default inline content and the properties they support: + +#### Styled Text + +`StyledText` is a type of `InlineContent` used to display pieces of text with styles: + +```typescript +type Styles = { + bold: true; + italic: true; + underline: true; + strikethrough: true; + textColor: string; + backgroundColor: string; +} + +type StyledText = { + type: "text"; + text: string; + styles: Styles; +}; +``` + +#### Link + +`Link` objects represent links to a URL: + +```typescript +type Link = { + type: "link"; + content: StyledText[]; + href: string; +}; +``` + +### Default Inline Content Properties + +TODO: This section only makes sense with the proposal in [Inline Content](/docs/editor-basics/content-structure#inline-content) + +There are some default inline content props that BlockNote uses for its default inline content types, and also exports for use in your own [custom inline content](/docs/custom-schemas/custom-inline-content): + +```typescript +type Styles = { + bold: true; + italic: true; + underline: true; + strikethrough: true; + textColor: string; + backgroundColor: string; +} + +type DefaultInlineContentProps = { + styles: Styles +}; +``` + +`styles:` The text styles applied to the inline content. Only useful for editable inline content types. + +### Creating New Inline Content Types + +You can create your own custom inline content using React - skip to [Custom Inline Content](/docs/custom-schemas/custom-inline-content) to learn how to do this. -Skip to [Custom Blocks](/docs/custom-schemas/custom-blocks) to learn how to do this. \ No newline at end of file +You can also create custom styles to apply to `StyledText` - skip to [Custom Styles](/docs/custom-schemas/custom-styles) to learn how to do this. \ No newline at end of file diff --git a/docs/pages/docs/editor-basics/default-inline-content.mdx b/docs/pages/docs/editor-basics/default-inline-content.mdx deleted file mode 100644 index 3b7a37ca8f..0000000000 --- a/docs/pages/docs/editor-basics/default-inline-content.mdx +++ /dev/null @@ -1,73 +0,0 @@ ---- -title: Default Inline Content -description: Overview of all default inline content types and their properties in BlockNote. -imageTitle: Default Inline Content ---- - -# Default Inline Content - -BlockNote includes a number of built-in inline content types. The demo editor below displays all of them: - -TODO: demo - -## Reference - -Here's an overview of all default inline content and the properties they support: - -### Styled Text - -`StyledText` is a type of `InlineContent` used to display pieces of text with styles: - -```typescript -type Styles = { - bold: true; - italic: true; - underline: true; - strikethrough: true; - textColor: string; - backgroundColor: string; -} - -type StyledText = { - type: "text"; - text: string; - styles: Styles; -}; -``` - -### Links - -`Link` objects represent links to a URL: - -```typescript -type Link = { - type: "link"; - content: StyledText[]; - href: string; -}; -``` - -## Default Inline Content Properties - -There are some default inline content props that BlockNote uses for its default inline content types, and also exports for use in your own [custom inline content](/docs/custom-schemas/custom-inline-content): - -```typescript -type Styles = { - bold: true; - italic: true; - underline: true; - strikethrough: true; - textColor: string; - backgroundColor: string; -} - -type DefaultInlineContentProps = { - styles: Styles -}; -``` - -`styles:` The text styles applied to the inline content. Only useful for editable inline content types. - -## Creating New Inline Content Types - -You can create your own custom blocks using React. Skip to [Custom Inline Content](/docs/custom-schemas/custom-inline-content) to learn how to do this. \ No newline at end of file diff --git a/docs/pages/docs/editor-basics/editor.mdx b/docs/pages/docs/editor-basics/editor.mdx index 5349080d0d..46f4ddb575 100644 --- a/docs/pages/docs/editor-basics/editor.mdx +++ b/docs/pages/docs/editor-basics/editor.mdx @@ -1,6 +1,6 @@ --- title: Editor Setup -description: While BlockNote is ready to use out-of-the-box, there are a number of ways in which you can set it up, to better suit your use case. +description: While BlockNote is ready to use out-of-the-box, there are a number of setup options you can choose to fit your use case. imageTitle: Editor Setup path: /docs/editor-basics/editor --- @@ -18,13 +18,13 @@ TODO: # Editor Setup -While BlockNote is ready to use out-of-the-box, there are a number of ways in which you can set it up, to better suit your use case. +While BlockNote is ready to use out-of-the-box, there are a number of setup options you can choose to fit your use case. ## Creating the Editor ### Using a React Hook -Most of the time, you'll want to create the BlockNote editor using the `useCreateBlockNote` hook. This will create a new editor as your React component gets mounted, and you can pass a dependency array as with other React hooks. +Most of the time, you'll want to create the BlockNote editor using the `useCreateBlockNote` hook. This will create a new editor when your React component gets mounted, and accepts a dependency array as with other React hooks. ```ts useCreateBlockNote = ( @@ -101,4 +101,48 @@ return ### Props -TODO +There are a number of additional props you can pass to `BlockNoteView`. You can find the full list of these below: + +```typescript +export type BlockNoteViewProps = Partial<{ + formattingToolbar?: boolean; + hyperlinkToolbar?: boolean; + sideMenu?: boolean; + slashMenu?: boolean; + imageToolbar?: boolean; + tableHandles?: boolean; + theme: + | "light" + | "dark" + | Theme + | { + light: Theme; + dark: Theme; + }; + editable?: boolean; + onSelectionChange?: () => void; + onChange?: () => void; +}>; +``` + +`formattingToolbar`: Whether the [Formatting Toolbar](/docs/ui-components/formatting-toolbar) should be enabled. + +`hyperlinkToolbar`: Whether the Hyperlink Toolbar should be enabled. + +`sideMenu`: Whether the [Block Side Menu](/docs/ui-components/side-menu) should be enabled. + +`slashMenu`: Whether the [Slash Menu](/docs/ui-components/suggestion-menus#slash-menu) should be enabled. + +`imageToolbar`: Whether the Image Toolbar should be enabled. + +`tableHandles`: Whether the Table Handles should be enabled. + +`theme`: The editor's theme, see [Themes](/docs/styling-theming/themes) for more about this. + +`editable`: Whether the editor should be editable. + +`onSelectionChange`: Callback for when the editor selection changes. + +`onChange`: Callback for when the editor selection or content changes. + +`BlockNoteView` also takes props that you can pass to any HTML `div` element. \ No newline at end of file diff --git a/docs/pages/docs/editor-basics/inline-content.mdx b/docs/pages/docs/editor-basics/inline-content.mdx deleted file mode 100644 index bf22efda21..0000000000 --- a/docs/pages/docs/editor-basics/inline-content.mdx +++ /dev/null @@ -1,74 +0,0 @@ ---- -title: Introduction to Blocks -description: Once you create an editor, you'll need to understand how its content is structured into blocks. -imageTitle: Introduction to Blocks ---- - -import { Example } from "@/components/example"; - -# Intro to Inline Content - -Many blocks contain inline content. This page explains what inline content is, and how it's represented in code. - -## Inline Content Objects - -A block's content is referred to as inline content, and is used to represent rich text. - -TODO: Between custom & default inline content types, the shapes on inline content objects are not standardized. -e.g. `Link`s have `href` and no props, custom IC will have props. `StyledText` can be IC itself, or used in other IC types. - -I think we should have smth like this: -```typescript -// Helper types -type Styles = { - bold: boolean; - textColor: string; - ... -} - -// Inline Content types -type StyledText = { - type: "richText"; - props: { - styles: Styles; - } - editable: true; -} -type Link = { - type: "link"; - props: { - styles: Styles; - href: string; - } - editable: true; -} -type Mention = { - type: "mention"; - props: { - user: string; - } - editable: false; -} -``` -This is way cleaner, but means that links cannot have mixed styles. I think we either: -- Convert links to styles (they're already marks in TipTap) -- Not do anything bc I don't think having mixed styles on links is ever useful - -## Table Content - -Most blocks use an array of `InlineContent` objects to describe their content, but tables are slightly different. Table blocks have `TableContent` as their content, in which each row contains an array of cells, that each have an array of `InlineContent` objects. - -```typescript -type TableContent = { - type: "tableContent"; - rows: { - cells: InlineContent[][]; - }[]; -}; -``` - -## Displaying Block Content - -In the example below, you can see how a block with the content, "Hello __there__, [BlockNote](https://www.blocknotejs.org)", is represented as an array of `InlineContent` objects: - -TODO: DEMO diff --git a/examples/01-basic/02-block-objects/App.tsx b/examples/01-basic/02-block-objects/App.tsx index 305c41ec18..1939ccdad2 100644 --- a/examples/01-basic/02-block-objects/App.tsx +++ b/examples/01-basic/02-block-objects/App.tsx @@ -7,7 +7,25 @@ export default function App() { // Stores the editor's contents as an array of Block objects. const [blocks, setBlocks] = useState([]); // Creates a new editor instance. - const editor = useCreateBlockNote(); + const editor = useCreateBlockNote({ + initialContent: [ + { + type: "paragraph", + content: "Welcome to this demo!", + }, + { + type: "heading", + content: "This is a heading block", + }, + { + type: "paragraph", + content: "This is a paragraph block", + }, + { + type: "paragraph", + }, + ], + }); // Renders the editor instance and its contents, as an array of Block // objects, below. diff --git a/examples/01-basic/02-block-objects/README.md b/examples/01-basic/02-block-objects/README.md index 7a1c328f88..fc20420f90 100644 --- a/examples/01-basic/02-block-objects/README.md +++ b/examples/01-basic/02-block-objects/README.md @@ -1,8 +1,6 @@ # Displaying Block Objects -Here, we use the `onChange` handler to listen to updates to the BlockNote document and display them below the editor. - -**Try it out:** Type in the editor and see the JSON representation of the document. +In this example, below the editor, we display its contents as they're represented in code - i.e. as a JSON array of `Block` objects. **Relevant Docs:** diff --git a/examples/01-basic/all-blocks/.bnexample.json b/examples/01-basic/all-blocks/.bnexample.json new file mode 100644 index 0000000000..0993be7115 --- /dev/null +++ b/examples/01-basic/all-blocks/.bnexample.json @@ -0,0 +1,4 @@ +{ + "playground": true, + "docs": true +} diff --git a/examples/01-basic/all-blocks/App.tsx b/examples/01-basic/all-blocks/App.tsx new file mode 100644 index 0000000000..9063303e84 --- /dev/null +++ b/examples/01-basic/all-blocks/App.tsx @@ -0,0 +1,53 @@ +import { BlockNoteView, useCreateBlockNote } from "@blocknote/react"; +import "@blocknote/react/style.css"; + +export default function App() { + // Creates a new editor instance. + const editor = useCreateBlockNote({ + initialContent: [ + { + type: "paragraph", + content: "Welcome to this demo!", + }, + { + type: "paragraph", + content: "Paragraph", + }, + { + type: "heading", + content: "Heading", + }, + { + type: "bulletListItem", + content: "Bullet List Item", + }, + { + type: "numberedListItem", + content: "Numbered List Item", + }, + { + type: "image", + }, + { + type: "table", + content: { + type: "tableContent", + rows: [ + { + cells: ["Table Cell", "Table Cell", "Table Cell"], + }, + { + cells: ["Table Cell", "Table Cell", "Table Cell"], + }, + { + cells: ["Table Cell", "Table Cell", "Table Cell"], + }, + ], + }, + }, + ], + }); + + // Renders the editor instance using a React component. + return ; +} diff --git a/examples/01-basic/all-blocks/README.md b/examples/01-basic/all-blocks/README.md new file mode 100644 index 0000000000..d84f931110 --- /dev/null +++ b/examples/01-basic/all-blocks/README.md @@ -0,0 +1,7 @@ +# All Block Types + +In this example, the editor contains each of BlockNote's built-in block types. + +**Relevant Docs:** + +- TODO diff --git a/examples/01-basic/all-inline-content/.bnexample.json b/examples/01-basic/all-inline-content/.bnexample.json new file mode 100644 index 0000000000..0993be7115 --- /dev/null +++ b/examples/01-basic/all-inline-content/.bnexample.json @@ -0,0 +1,4 @@ +{ + "playground": true, + "docs": true +} diff --git a/examples/01-basic/all-inline-content/App.tsx b/examples/01-basic/all-inline-content/App.tsx new file mode 100644 index 0000000000..585db97021 --- /dev/null +++ b/examples/01-basic/all-inline-content/App.tsx @@ -0,0 +1,45 @@ +import { BlockNoteView, useCreateBlockNote } from "@blocknote/react"; +import "@blocknote/react/style.css"; + +export default function App() { + // Creates a new editor instance. + const editor = useCreateBlockNote({ + initialContent: [ + { + type: "paragraph", + content: "Welcome to this demo!", + }, + { + type: "paragraph", + content: [ + { + type: "text", + text: "Styled Text", + styles: { + bold: true, + italic: true, + textColor: "red", + backgroundColor: "blue", + }, + }, + { + type: "text", + text: " ", + styles: {}, + }, + { + type: "link", + content: "Link", + href: "https://www.blocknotejs.org", + }, + ], + }, + { + type: "paragraph", + }, + ], + }); + + // Renders the editor instance using a React component. + return ; +} diff --git a/examples/01-basic/all-inline-content/README.md b/examples/01-basic/all-inline-content/README.md new file mode 100644 index 0000000000..4dff460052 --- /dev/null +++ b/examples/01-basic/all-inline-content/README.md @@ -0,0 +1,8 @@ +# All Inline Content Types + +In this example, the editor contains each of BlockNote's built-in inline content +types. + +**Relevant Docs:** + +- TODO diff --git a/examples/01-basic/inline-content-objects/.bnexample.json b/examples/01-basic/inline-content-objects/.bnexample.json new file mode 100644 index 0000000000..0993be7115 --- /dev/null +++ b/examples/01-basic/inline-content-objects/.bnexample.json @@ -0,0 +1,4 @@ +{ + "playground": true, + "docs": true +} diff --git a/examples/01-basic/inline-content-objects/App.tsx b/examples/01-basic/inline-content-objects/App.tsx new file mode 100644 index 0000000000..a06785f6b5 --- /dev/null +++ b/examples/01-basic/inline-content-objects/App.tsx @@ -0,0 +1,71 @@ +import { + DefaultInlineContentSchema, + DefaultStyleSchema, + InlineContent, + TableContent, +} from "@blocknote/core"; +import { BlockNoteView, useCreateBlockNote } from "@blocknote/react"; +import "@blocknote/react/style.css"; +import { useState } from "react"; + +export default function App() { + // Stores the editor's contents as an array of Block objects. + // TODO: InlineContent and TableContent should have default type args + const [inlineContent, setInlineContent] = useState< + ( + | InlineContent[] + | TableContent + | undefined + )[] + >([]); + // Creates a new editor instance. + const editor = useCreateBlockNote({ + initialContent: [ + { + type: "paragraph", + content: "Welcome to this demo!", + }, + { + type: "paragraph", + content: [ + { + type: "text", + text: "Hello ", + styles: {}, + }, + { + type: "text", + text: "there, ", + styles: { + bold: true, + }, + }, + { + type: "link", + content: "BlockNote", + href: "https://www.blocknotejs.org", + }, + ], + }, + { + type: "paragraph", + }, + ], + }); + + // Renders the editor instance and the contents of its blocks below. + return ( +
+ { + // Converts the editor's contents to an array with each top level + // block's content. + setInlineContent(editor.topLevelBlocks.map((block) => block.content)); + }} + /> +

Document JSON:

+
{JSON.stringify(inlineContent, null, 2)}
+
+ ); +} diff --git a/examples/01-basic/inline-content-objects/README.md b/examples/01-basic/inline-content-objects/README.md new file mode 100644 index 0000000000..7c2641a47d --- /dev/null +++ b/examples/01-basic/inline-content-objects/README.md @@ -0,0 +1,7 @@ +# Displaying Inline Content Objects + +In this example, below the editor, we display the contents of each block as they're represented in code. This changes based on block type, but is always valid JSON. + +**Relevant Docs:** + +- TODO diff --git a/examples/01-basic/inline-content-objects/styles.css b/examples/01-basic/inline-content-objects/styles.css new file mode 100644 index 0000000000..a7753b012f --- /dev/null +++ b/examples/01-basic/inline-content-objects/styles.css @@ -0,0 +1,3 @@ +pre { + color: grey; +} \ No newline at end of file From 2f80b56959db04108bfb515eb8dd3fbebbcb12a3 Mon Sep 17 00:00:00 2001 From: yousefed Date: Mon, 26 Feb 2024 16:04:45 +0100 Subject: [PATCH 3/9] fix build --- docs/package.json | 2 +- packages/dev-scripts/examples/genDocs.ts | 57 +++++++++++------------- 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/docs/package.json b/docs/package.json index 3b1d5147ac..8ec05f7036 100644 --- a/docs/package.json +++ b/docs/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "dev": "next dev", - "build": "next build", + "docs:build": "next build", "start": "next start", "lint": "next lint" }, diff --git a/packages/dev-scripts/examples/genDocs.ts b/packages/dev-scripts/examples/genDocs.ts index 384db3d4ac..4f31ff41c5 100644 --- a/packages/dev-scripts/examples/genDocs.ts +++ b/packages/dev-scripts/examples/genDocs.ts @@ -43,17 +43,19 @@ ${file.code} `; +const COMPONENT_DIR = path.resolve( + dir, + "../../../docs/components/example/generated/" +); + +const EXAMPLES_PAGES_DIR = path.resolve(dir, "../../../docs/pages/examples/"); + /** * Generates the component that has all the source code of the example * This block can be used both in the /docs and in the /example page */ async function generateCodeForExample(project: Project) { - const target = path.resolve( - dir, - "../../../docs/components/example/generated/mdx/" + - project.fullSlug + - ".mdx" - ); + const target = path.join(COMPONENT_DIR, "mdx", project.fullSlug + ".mdx"); const files = getProjectFiles(project); const filtered = Object.fromEntries( @@ -82,10 +84,7 @@ ${readme} * Consists of the contents of the readme + the interactive example */ async function generatePageForExample(project: Project) { - const target = path.resolve( - dir, - "../../../docs/pages/examples/" + project.fullSlug + ".mdx" - ); + const target = path.join(EXAMPLES_PAGES_DIR, project.fullSlug + ".mdx"); const files = getProjectFiles(project); @@ -102,20 +101,11 @@ async function generateMetaForExampleGroup(group: { slug: string; projects: Project[]; }) { - if ( - !fs.existsSync( - path.resolve(dir, "../../../docs/pages/examples/" + group.slug) - ) - ) { - fs.mkdirSync( - path.resolve(dir, "../../../docs/pages/examples/" + group.slug) - ); + if (!fs.existsSync(path.join(EXAMPLES_PAGES_DIR, group.slug))) { + fs.mkdirSync(path.join(EXAMPLES_PAGES_DIR, group.slug)); } - const target = path.resolve( - dir, - "../../../docs/pages/examples/" + group.slug + "/_meta.json" - ); + const target = path.join(EXAMPLES_PAGES_DIR, group.slug, "_meta.json"); const meta = Object.fromEntries( group.projects.map((project) => [ @@ -160,10 +150,7 @@ ${projects * Generate the file with all the dynamic imports for examples (exampleComponents.gen.tsx) */ async function generateExampleComponents(projects: Project[]) { - const target = path.resolve( - dir, - "../../../docs/components/example/generated/exampleComponents.gen.tsx" - ); + const target = path.join(COMPONENT_DIR, "exampleComponents.gen.tsx"); const code = templateExampleComponents(projects); @@ -174,10 +161,7 @@ async function generateExampleComponents(projects: Project[]) { * generates exampleList.gen.ts file with data about all the examples */ async function generateExampleList(projects: Project[]) { - const target = path.resolve( - dir, - "../../../docs/components/example/generated/exampleList.gen.ts" - ); + const target = path.join(COMPONENT_DIR, "exampleList.gen.ts"); const groups = groupProjects(projects); @@ -199,6 +183,19 @@ async function generateExampleList(projects: Project[]) { fs.writeFileSync(target, code); } +// clean old files / dirs +fs.rmSync(COMPONENT_DIR, { recursive: true, force: true }); + +fs.readdirSync(EXAMPLES_PAGES_DIR, { withFileTypes: true }).forEach((file) => { + if (file.isDirectory()) { + fs.rmSync(path.join(EXAMPLES_PAGES_DIR, file.name), { + recursive: true, + force: true, + }); + } +}); + +// generate new files const projects = getExampleProjects(); // TODO: .filter((p) => p.config?.docs === true); const groups = groupProjects(projects); From f407230e00962626c8b0e45cfcb9393cad6b0528 Mon Sep 17 00:00:00 2001 From: yousefed Date: Mon, 26 Feb 2024 16:21:59 +0100 Subject: [PATCH 4/9] fix --- docs/api/og.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/api/og.tsx b/docs/api/og.tsx index b7794f6e1b..96e4514e02 100644 --- a/docs/api/og.tsx +++ b/docs/api/og.tsx @@ -1,12 +1,11 @@ import { ImageResponse } from "@vercel/og"; -import React from "react"; export const config = { runtime: "edge", }; const font = fetch( - new URL("../docs/public/fonts/Gilroy-Regular.ttf", import.meta.url) + new URL("../public/fonts/Gilroy-Regular.ttf", import.meta.url), ).then((res) => res.arrayBuffer()); export default async function handler(request: { url: string }) { From f6d82f32fd429bdb204551cbc530f7e7875e8f1e Mon Sep 17 00:00:00 2001 From: Matthew Lipski Date: Mon, 26 Feb 2024 16:24:37 +0100 Subject: [PATCH 5/9] Updated editor api links --- .../docs/editor-api/cursor-selections.mdx | 3 +- .../docs/editor-api/manipulating-blocks.mdx | 49 +++--- .../manipulating-inline-content.mdx | 145 +----------------- 3 files changed, 29 insertions(+), 168 deletions(-) diff --git a/docs/pages/docs/editor-api/cursor-selections.mdx b/docs/pages/docs/editor-api/cursor-selections.mdx index 571a9b6e74..83f3429308 100644 --- a/docs/pages/docs/editor-api/cursor-selections.mdx +++ b/docs/pages/docs/editor-api/cursor-selections.mdx @@ -5,6 +5,7 @@ imageTitle: Cursor & Selections path: /docs/cursor-selections --- + import { Example } from "@/components/example"; # Cursor & Selections @@ -59,7 +60,7 @@ setTextCursorPosition( editor.setTextCursorPosition(targetBlock, placement); ``` -`targetBlock:` The [identifier](/docs/manipulating-blocks#block-identifiers) of an existing block that the text cursor should be moved to. +`targetBlock:` The [identifier](/docs/editor-api/manipulating-blocks#block-identifiers) of an existing block that the text cursor should be moved to. `placement:` Whether the text cursor should be placed at the start or end of the block. diff --git a/docs/pages/docs/editor-api/manipulating-blocks.mdx b/docs/pages/docs/editor-api/manipulating-blocks.mdx index 3b72676d1b..3f9dc69f65 100644 --- a/docs/pages/docs/editor-api/manipulating-blocks.mdx +++ b/docs/pages/docs/editor-api/manipulating-blocks.mdx @@ -9,18 +9,17 @@ path: /docs/manipulating-blocks Below, we explain the methods on `editor` you can use to read Blocks from the editor, and how to create / remove / update Blocks: -- topLevelBlocks -- getBlock -- forEachBlock -- insertBlocks -- updateBlock -- removeBlocks -- replaceBlocks -- canNestBlock -- nestBlock -- canUnnestBlock - -(TODO: link) +- [topLevelBlocks](/docs/editor-api/manipulating-blocks#getting-all-top-level-blocks) +- [getBlock](/docs/editor-api/manipulating-blocks#getting-a-specific-block) +- [forEachBlock](/docs/editor-api/manipulating-blocks#traversing-all-blocks) +- [insertBlocks](/docs/editor-api/manipulating-blocks#inserting-new-blocks) +- [updateBlock](/docs/editor-api/manipulating-blocks#updating-blocks) +- [removeBlocks](/docs/editor-api/manipulating-blocks#removing-blocks) +- [replaceBlocks](/docs/editor-api/manipulating-blocks#replacing-blocks) +- [canNestBlock](/docs/editor-api/manipulating-blocks#nesting-blocks) +- [nestBlock](/docs/editor-api/manipulating-blocks#nesting-blocks) +- [canUnnestBlock](/docs/editor-api/manipulating-blocks#un-nesting-blocks) +- [unnestBlock](/docs/editor-api/manipulating-blocks#un-nesting-blocks) ## Common types @@ -70,7 +69,7 @@ const blocks = editor.topLevelBlocks; `returns:` The document; a snapshot of all top-level (non-nested) blocks in the editor. -We already used this for the demo in [Getting Familiar with Block Objects](/docs/blocks#demo-getting-familiar-with-block-objects), to show the JSON representation of blocks below the editor. +We already used this for the [Editor Content in JSON](/docs/editor-basics/content-structure#editor-content-in-json) demo. ### Getting a Specific Block @@ -83,7 +82,7 @@ getBlock(blockIdentifier: BlockIdentifier): Block | undefined; const block = editor.getBlock(blockIdentifier); ``` -`blockIdentifier:` The [identifier](#block-identifiers) of an existing block that should be retrieved. +`blockIdentifier:` The [identifier](/docs/editor-api/manipulating-blocks#block-identifiers) of an existing block that should be retrieved. `returns:` The block that matches the identifier, or `undefined` if no matching block was found. @@ -107,7 +106,7 @@ editor.forEachBlock((block) => {...}); ### Getting the hovered / selected Block -See [Cursor & Selections](/docs/cursor-selections) to learn how to retrieve the block a user is interacting with. +See [Cursor & Selections](/docs/editor-api/cursor-selections) to learn how to retrieve the block a user is interacting with. ## Inserting New Blocks @@ -124,9 +123,9 @@ insertBlocks( editor.insertBlocks([{type: "paragraph", text: "Hello World"}], referenceBlock, placement) ``` -`blocksToInsert:` An array of [partial blocks](#partial-blocks) that should be inserted. +`blocksToInsert:` An array of [partial blocks](/docs/editor-api/manipulating-blocks#partial-blocks) that should be inserted. -`referenceBlock:` An [identifier](#block-identifiers) for an existing block, at which the new blocks should be inserted. +`referenceBlock:` An [identifier](/docs/editor-api/manipulating-blocks#block-identifiers) for an existing block, at which the new blocks should be inserted. `placement:` Whether the blocks should be inserted just before, just after, or nested inside the `referenceBlock`. Inserts the blocks at the start of the existing block's children if `"nested"` is used. @@ -148,9 +147,9 @@ updateBlock( editor.updateBlock(blockToUpdate, { type: "paragraph" }); ``` -`blockToUpdate:` The [identifier](#block-identifiers) of an existing block that should be updated. +`blockToUpdate:` The [identifier](/docs/editor-api/manipulating-blocks#block-identifiers) of an existing block that should be updated. -`update:` A [partial block](#partial-blocks) which defines how the existing block should be changed. +`update:` A [partial blocks](/docs/editor-api/manipulating-blocks#partial-blocks) which defines how the existing block should be changed. Since `blockToUpdate` is a `PartialBlock` object, some fields might not be defined. These undefined fields are kept as-is from the existing block. @@ -169,7 +168,7 @@ removeBlocks( editor.removeBlocks(blocksToRemove) ``` -`blocksToRemove:` An array of [identifiers](#block-identifiers) for existing blocks that should be removed. +`blocksToRemove:` An array of [identifier](/docs/editor-api/manipulating-blocks#block-identifiers) for existing blocks that should be removed. Throws an error if any of the blocks could not be found. @@ -187,9 +186,9 @@ replaceBlocks( editor.replaceBlocks(blocksToRemove, blocksToInsert) ``` -`blocksToRemove:` An array of [identifiers](#block-identifiers) for existing blocks that should be replaced. +`blocksToRemove:` An array of [identifier](/docs/editor-api/manipulating-blocks#block-identifiers) for existing blocks that should be replaced. -`blocksToInsert:` An array of [partial blocks](#partial-blocks) that the existing ones should be replaced with. +`blocksToInsert:` An array of [partial blocks](/docs/editor-api/manipulating-blocks#partial-blocks) that the existing ones should be replaced with. If the blocks that should be removed are not adjacent or are at different nesting levels, `blocksToInsert` will be inserted at the position of the first block in `blocksToRemove`. @@ -197,13 +196,13 @@ Throws an error if any of the blocks to remove could not be found. ## Nesting & Un-nesting Blocks -BlockNote also provides functions to nest & un-nest the block containing the [Text Cursor](/docs/cursor-selections#text-cursor). +BlockNote also provides functions to nest & un-nest the block containing the [Text Cursor](/docs/editor-api/cursor-selections#text-cursor). ### Nesting Blocks TODO: this API seems different as it doesn't require a block id. should we make it possible to pass an identifier? -Use `canNestBlock` to check whether the block containing the [Text Cursor](/docs/cursor-selections#text-cursor) can be nested (i.e. if there is a block above it at the same nesting level): +Use `canNestBlock` to check whether the block containing the [Text Cursor](/docs/editor-api/cursor-selections#text-cursor) can be nested (i.e. if there is a block above it at the same nesting level): ```typescript canNestBlock(): boolean; @@ -223,7 +222,7 @@ editor.nestBlock(); ### Un-nesting Blocks -Use `canUnnestBlock` to check whether the block containing the [Text Cursor](/docs/cursor-selections#text-cursor) can be un-nested (i.e. if it's nested in another block): +Use `canUnnestBlock` to check whether the block containing the [Text Cursor](/docs/editor-api/cursor-selections#text-cursor) can be un-nested (i.e. if it's nested in another block): ```typescript canUnnestBlock(): boolean; diff --git a/docs/pages/docs/editor-api/manipulating-inline-content.mdx b/docs/pages/docs/editor-api/manipulating-inline-content.mdx index 738d5754b5..b0ead98c1d 100644 --- a/docs/pages/docs/editor-api/manipulating-inline-content.mdx +++ b/docs/pages/docs/editor-api/manipulating-inline-content.mdx @@ -5,152 +5,13 @@ imageTitle: Manipulating Inline Content path: /docs/block-content --- -TODO: API For this is pretty weird vs blocks - -[//]: # (# Block Content) - -[//]: # () -[//]: # (The `content` field of a `Block` describes the content of the block. For all blocks except tables, this field is an array of `InlineContent` objects and describes the rich text content typed inside the block.) - -[//]: # () -[//]: # (In the example below, you can see how a sentence like "Hello __there__, [BlockNote](https://www.blocknotejs.org)" is represented as an array of `InlineContent` objects:) - -[//]: # () -[//]: # (TODO: DEMO) - -[//]: # () -[//]: # () -[//]: # (## `InlineContent` Types) - -[//]: # () -[//]: # (`InlineContent` can be styled text (rich text), a link, or a mention:) - -[//]: # () -[//]: # (### Styled Text) - -[//]: # () -[//]: # (`StyledText` is a type of `InlineContent` used to display pieces of text with styles:) - -[//]: # () -[//]: # (```typescript) - -[//]: # (type StyledText = {) - -[//]: # ( type: "text";) - -[//]: # ( text: string;) - -[//]: # ( styles: Styles;) - -[//]: # (};) - -[//]: # (```) - -[//]: # () -[//]: # (`text:` The displayed text.) - -[//]: # () -[//]: # (`styles:` The styles that are applied to the text.) - -[//]: # () -[//]: # (**Styles Object**) - -[//]: # () -[//]: # (`StyledText` supports a variety of styles, including bold, underline, and text color, which are represented using a `Styles` object:) - -[//]: # () -[//]: # (```typescript) - -[//]: # (type Styles = Partial<{) - -[//]: # ( bold: true;) - -[//]: # ( italic: true;) - -[//]: # ( underline: true;) - -[//]: # ( strikethrough: true;) - -[//]: # ( textColor: string;) - -[//]: # ( backgroundColor: string;) - -[//]: # (}>;) - -[//]: # (```) - -[//]: # () -[//]: # (### Links) - -[//]: # () -[//]: # (`Link` objects represent links to a URL:) - -[//]: # () -[//]: # (```typescript) - -[//]: # (type Link = {) - -[//]: # ( type: "link";) - -[//]: # ( content: StyledText[];) - -[//]: # ( href: string;) - -[//]: # (};) - -[//]: # (```) - -[//]: # () -[//]: # (`content:` The styled text used to display the link.) - -[//]: # () -[//]: # (`href:` The URL that opens when clicking the link.) - -[//]: # () -[//]: # (### Mentions) - -[//]: # () -[//]: # (TODO) - -[//]: # () -[//]: # (### Custom `InlineContent` and `Styles`) - -[//]: # () -[//]: # (You can create your own types of `InlineContent` and `Styles` using React. Skip to the Advanced "custom schemas" section to learn more about extending the editor. (TODO: link)) - -[//]: # () -[//]: # (## TableContent) - -[//]: # () -[//]: # (Most blocks use an array of `InlineContent` objects to describe their content, but tables are slightly different. Table blocks have `TableContent` as their content, in which each row contains an array of cells, that) - -[//]: # () -[//]: # () -[//]: # (```typescript) - -[//]: # (type TableContent = {) - -[//]: # ( type: "tableContent";) - -[//]: # ( rows: {) - -[//]: # ( cells: InlineContent[][];) - -[//]: # ( }[];) - -[//]: # (};) - -[//]: # (```) - # Manipulating Inline Content -TODO: where should this be? - While `InlineContent` objects are used to describe a block's content, they can be cumbersome to work with directly. Therefore, BlockNote exposes functions which make it easier to edit block contents. ## Accessing Styles -You can get the styles at the current [Text Cursor Position](/docs/cursor-selections#text-cursor) using the following function: +You can get the styles at the current [Text Cursor Position](/docs/editor-api/cursor-selections#text-cursor) using the following function: ```typescript getActiveStyles(): Styles; @@ -159,7 +20,7 @@ getActiveStyles(): Styles; const styles = editor.getActiveStyles(); ``` -If a [Selection](/docs/cursor-selections#selections) is active, this function returns the active styles at the end of the selection. +If a [Selection](/docs/editor-api/cursor-selections#selections) is active, this function returns the active styles at the end of the selection. ## Adding Styles @@ -229,4 +90,4 @@ createLink(url: string, text?: string): void; editor.createLink("https://www.blocknotejs.org/", "BlockNote"); ``` -If a [Selection](/docs/cursor-selections#selections) is active, the new link will replace the currently selected content. +If a [Selection](/docs/editor-api/cursor-selections#selections) is active, the new link will replace the currently selected content. From 30ba0d7e8fe6a563aa8bd60c8cd852754070f0eb Mon Sep 17 00:00:00 2001 From: yousefed Date: Mon, 26 Feb 2024 16:27:04 +0100 Subject: [PATCH 6/9] generate + clean some comments --- docs/pages/docs/editor-basics/editor.mdx | 21 ++++------ examples/01-basic/all-blocks/index.html | 14 +++++++ examples/01-basic/all-blocks/main.tsx | 11 +++++ examples/01-basic/all-blocks/package.json | 34 +++++++++++++++ examples/01-basic/all-blocks/tsconfig.json | 36 ++++++++++++++++ examples/01-basic/all-blocks/vite.config.ts | 32 ++++++++++++++ .../01-basic/all-inline-content/index.html | 14 +++++++ examples/01-basic/all-inline-content/main.tsx | 11 +++++ .../01-basic/all-inline-content/package.json | 34 +++++++++++++++ .../01-basic/all-inline-content/tsconfig.json | 36 ++++++++++++++++ .../all-inline-content/vite.config.ts | 32 ++++++++++++++ .../inline-content-objects/index.html | 14 +++++++ .../01-basic/inline-content-objects/main.tsx | 11 +++++ .../inline-content-objects/package.json | 34 +++++++++++++++ .../inline-content-objects/tsconfig.json | 36 ++++++++++++++++ .../inline-content-objects/vite.config.ts | 32 ++++++++++++++ playground/src/examples.gen.tsx | 42 +++++++++++++++++++ 17 files changed, 431 insertions(+), 13 deletions(-) create mode 100644 examples/01-basic/all-blocks/index.html create mode 100644 examples/01-basic/all-blocks/main.tsx create mode 100644 examples/01-basic/all-blocks/package.json create mode 100644 examples/01-basic/all-blocks/tsconfig.json create mode 100644 examples/01-basic/all-blocks/vite.config.ts create mode 100644 examples/01-basic/all-inline-content/index.html create mode 100644 examples/01-basic/all-inline-content/main.tsx create mode 100644 examples/01-basic/all-inline-content/package.json create mode 100644 examples/01-basic/all-inline-content/tsconfig.json create mode 100644 examples/01-basic/all-inline-content/vite.config.ts create mode 100644 examples/01-basic/inline-content-objects/index.html create mode 100644 examples/01-basic/inline-content-objects/main.tsx create mode 100644 examples/01-basic/inline-content-objects/package.json create mode 100644 examples/01-basic/inline-content-objects/tsconfig.json create mode 100644 examples/01-basic/inline-content-objects/vite.config.ts diff --git a/docs/pages/docs/editor-basics/editor.mdx b/docs/pages/docs/editor-basics/editor.mdx index 46f4ddb575..b4080f12a1 100644 --- a/docs/pages/docs/editor-basics/editor.mdx +++ b/docs/pages/docs/editor-basics/editor.mdx @@ -8,13 +8,9 @@ path: /docs/editor-basics/editor import { Example } from "@/components/example"; TODO: -[x] review API -[x] what needs to be part of useBlockNote vs the component? -[ ] document instantiation / useBlockNote [ ] expose / document hooks [ ] document events [ ] explain controlled / uncontrolled -[ ] Maybe move outside of "Editor API"? # Editor Setup @@ -29,7 +25,7 @@ Most of the time, you'll want to create the BlockNote editor using the `useCreat ```ts useCreateBlockNote = ( options?: Partial, - deps?: React.DependencyList + deps?: React.DependencyList, ) => BlockNoteEditor; ``` @@ -40,9 +36,8 @@ Read on to [Options](/docs/editor-basics/editor#options) to see what options you You can also create a new editor using `BlockNoteEditor.create`. You should use this when you want to wait after your React component gets mounted before creating the editor. ```ts -BlockNoteEditor.create = ( - options?: Partial, -) => BlockNoteEditor; +BlockNoteEditor.create = (options?: Partial) => + BlockNoteEditor; ``` In the demo below, we use `BlockNoteEditor.create` so we can fetch the editor's initial content from `localStorage` before creating it. @@ -96,7 +91,7 @@ To, render the editor, you should use the `BlockNoteView` component, and pass in ```tsx const editor = useCreateBlockNote(); -return +return ; ``` ### Props @@ -119,9 +114,9 @@ export type BlockNoteViewProps = Partial<{ light: Theme; dark: Theme; }; - editable?: boolean; - onSelectionChange?: () => void; - onChange?: () => void; + editable?: boolean; + onSelectionChange?: () => void; + onChange?: () => void; }>; ``` @@ -145,4 +140,4 @@ export type BlockNoteViewProps = Partial<{ `onChange`: Callback for when the editor selection or content changes. -`BlockNoteView` also takes props that you can pass to any HTML `div` element. \ No newline at end of file +`BlockNoteView` also takes props that you can pass to any HTML `div` element. diff --git a/examples/01-basic/all-blocks/index.html b/examples/01-basic/all-blocks/index.html new file mode 100644 index 0000000000..cd00e7388b --- /dev/null +++ b/examples/01-basic/all-blocks/index.html @@ -0,0 +1,14 @@ + + + + + + All Block Types + + +
+ + + diff --git a/examples/01-basic/all-blocks/main.tsx b/examples/01-basic/all-blocks/main.tsx new file mode 100644 index 0000000000..f88b490fbd --- /dev/null +++ b/examples/01-basic/all-blocks/main.tsx @@ -0,0 +1,11 @@ +// AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY +import React from "react"; +import { createRoot } from "react-dom/client"; +import App from "./App"; + +const root = createRoot(document.getElementById("root")!); +root.render( + + + +); diff --git a/examples/01-basic/all-blocks/package.json b/examples/01-basic/all-blocks/package.json new file mode 100644 index 0000000000..953d7ba6aa --- /dev/null +++ b/examples/01-basic/all-blocks/package.json @@ -0,0 +1,34 @@ +{ + "name": "@blocknote/example-all-blocks", + "description": "AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY", + "private": true, + "version": "0.11.1", + "scripts": { + "start": "vite", + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "lint": "eslint . --max-warnings 0" + }, + "dependencies": { + "@blocknote/core": "^0.11.1", + "@blocknote/react": "^0.11.1", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/react": "^18.0.25", + "@types/react-dom": "^18.0.9", + "@vitejs/plugin-react": "^4.0.4", + "eslint": "^8.10.0", + "vite": "^4.4.8" + }, + "eslintConfig": { + "extends": [ + "../../../.eslintrc.js" + ] + }, + "eslintIgnore": [ + "dist" + ] +} \ No newline at end of file diff --git a/examples/01-basic/all-blocks/tsconfig.json b/examples/01-basic/all-blocks/tsconfig.json new file mode 100644 index 0000000000..bb6637c459 --- /dev/null +++ b/examples/01-basic/all-blocks/tsconfig.json @@ -0,0 +1,36 @@ +{ + "__comment": "AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY", + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "lib": [ + "DOM", + "DOM.Iterable", + "ESNext" + ], + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "composite": true + }, + "include": [ + "." + ], + "references": [ + { + "path": "../../../packages/core/" + }, + { + "path": "../../../packages/react/" + } + ] +} \ No newline at end of file diff --git a/examples/01-basic/all-blocks/vite.config.ts b/examples/01-basic/all-blocks/vite.config.ts new file mode 100644 index 0000000000..f62ab20bc2 --- /dev/null +++ b/examples/01-basic/all-blocks/vite.config.ts @@ -0,0 +1,32 @@ +// AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY +import react from "@vitejs/plugin-react"; +import * as fs from "fs"; +import * as path from "path"; +import { defineConfig } from "vite"; +// import eslintPlugin from "vite-plugin-eslint"; +// https://vitejs.dev/config/ +export default defineConfig((conf) => ({ + plugins: [react()], + optimizeDeps: {}, + build: { + sourcemap: true, + }, + resolve: { + alias: + conf.command === "build" || + !fs.existsSync(path.resolve(__dirname, "../../packages/core/src")) + ? {} + : ({ + // Comment out the lines below to load a built version of blocknote + // or, keep as is to load live from sources with live reload working + "@blocknote/core": path.resolve( + __dirname, + "../../packages/core/src/" + ), + "@blocknote/react": path.resolve( + __dirname, + "../../packages/react/src/" + ), + } as any), + }, +})); diff --git a/examples/01-basic/all-inline-content/index.html b/examples/01-basic/all-inline-content/index.html new file mode 100644 index 0000000000..8097927a03 --- /dev/null +++ b/examples/01-basic/all-inline-content/index.html @@ -0,0 +1,14 @@ + + + + + + All Inline Content Types + + +
+ + + diff --git a/examples/01-basic/all-inline-content/main.tsx b/examples/01-basic/all-inline-content/main.tsx new file mode 100644 index 0000000000..f88b490fbd --- /dev/null +++ b/examples/01-basic/all-inline-content/main.tsx @@ -0,0 +1,11 @@ +// AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY +import React from "react"; +import { createRoot } from "react-dom/client"; +import App from "./App"; + +const root = createRoot(document.getElementById("root")!); +root.render( + + + +); diff --git a/examples/01-basic/all-inline-content/package.json b/examples/01-basic/all-inline-content/package.json new file mode 100644 index 0000000000..12af279d98 --- /dev/null +++ b/examples/01-basic/all-inline-content/package.json @@ -0,0 +1,34 @@ +{ + "name": "@blocknote/example-all-inline-content", + "description": "AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY", + "private": true, + "version": "0.11.1", + "scripts": { + "start": "vite", + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "lint": "eslint . --max-warnings 0" + }, + "dependencies": { + "@blocknote/core": "^0.11.1", + "@blocknote/react": "^0.11.1", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/react": "^18.0.25", + "@types/react-dom": "^18.0.9", + "@vitejs/plugin-react": "^4.0.4", + "eslint": "^8.10.0", + "vite": "^4.4.8" + }, + "eslintConfig": { + "extends": [ + "../../../.eslintrc.js" + ] + }, + "eslintIgnore": [ + "dist" + ] +} \ No newline at end of file diff --git a/examples/01-basic/all-inline-content/tsconfig.json b/examples/01-basic/all-inline-content/tsconfig.json new file mode 100644 index 0000000000..bb6637c459 --- /dev/null +++ b/examples/01-basic/all-inline-content/tsconfig.json @@ -0,0 +1,36 @@ +{ + "__comment": "AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY", + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "lib": [ + "DOM", + "DOM.Iterable", + "ESNext" + ], + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "composite": true + }, + "include": [ + "." + ], + "references": [ + { + "path": "../../../packages/core/" + }, + { + "path": "../../../packages/react/" + } + ] +} \ No newline at end of file diff --git a/examples/01-basic/all-inline-content/vite.config.ts b/examples/01-basic/all-inline-content/vite.config.ts new file mode 100644 index 0000000000..f62ab20bc2 --- /dev/null +++ b/examples/01-basic/all-inline-content/vite.config.ts @@ -0,0 +1,32 @@ +// AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY +import react from "@vitejs/plugin-react"; +import * as fs from "fs"; +import * as path from "path"; +import { defineConfig } from "vite"; +// import eslintPlugin from "vite-plugin-eslint"; +// https://vitejs.dev/config/ +export default defineConfig((conf) => ({ + plugins: [react()], + optimizeDeps: {}, + build: { + sourcemap: true, + }, + resolve: { + alias: + conf.command === "build" || + !fs.existsSync(path.resolve(__dirname, "../../packages/core/src")) + ? {} + : ({ + // Comment out the lines below to load a built version of blocknote + // or, keep as is to load live from sources with live reload working + "@blocknote/core": path.resolve( + __dirname, + "../../packages/core/src/" + ), + "@blocknote/react": path.resolve( + __dirname, + "../../packages/react/src/" + ), + } as any), + }, +})); diff --git a/examples/01-basic/inline-content-objects/index.html b/examples/01-basic/inline-content-objects/index.html new file mode 100644 index 0000000000..19aee77636 --- /dev/null +++ b/examples/01-basic/inline-content-objects/index.html @@ -0,0 +1,14 @@ + + + + + + Displaying Inline Content Objects + + +
+ + + diff --git a/examples/01-basic/inline-content-objects/main.tsx b/examples/01-basic/inline-content-objects/main.tsx new file mode 100644 index 0000000000..f88b490fbd --- /dev/null +++ b/examples/01-basic/inline-content-objects/main.tsx @@ -0,0 +1,11 @@ +// AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY +import React from "react"; +import { createRoot } from "react-dom/client"; +import App from "./App"; + +const root = createRoot(document.getElementById("root")!); +root.render( + + + +); diff --git a/examples/01-basic/inline-content-objects/package.json b/examples/01-basic/inline-content-objects/package.json new file mode 100644 index 0000000000..e4666f13bc --- /dev/null +++ b/examples/01-basic/inline-content-objects/package.json @@ -0,0 +1,34 @@ +{ + "name": "@blocknote/example-inline-content-objects", + "description": "AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY", + "private": true, + "version": "0.11.1", + "scripts": { + "start": "vite", + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "lint": "eslint . --max-warnings 0" + }, + "dependencies": { + "@blocknote/core": "^0.11.1", + "@blocknote/react": "^0.11.1", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/react": "^18.0.25", + "@types/react-dom": "^18.0.9", + "@vitejs/plugin-react": "^4.0.4", + "eslint": "^8.10.0", + "vite": "^4.4.8" + }, + "eslintConfig": { + "extends": [ + "../../../.eslintrc.js" + ] + }, + "eslintIgnore": [ + "dist" + ] +} \ No newline at end of file diff --git a/examples/01-basic/inline-content-objects/tsconfig.json b/examples/01-basic/inline-content-objects/tsconfig.json new file mode 100644 index 0000000000..bb6637c459 --- /dev/null +++ b/examples/01-basic/inline-content-objects/tsconfig.json @@ -0,0 +1,36 @@ +{ + "__comment": "AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY", + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "lib": [ + "DOM", + "DOM.Iterable", + "ESNext" + ], + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "composite": true + }, + "include": [ + "." + ], + "references": [ + { + "path": "../../../packages/core/" + }, + { + "path": "../../../packages/react/" + } + ] +} \ No newline at end of file diff --git a/examples/01-basic/inline-content-objects/vite.config.ts b/examples/01-basic/inline-content-objects/vite.config.ts new file mode 100644 index 0000000000..f62ab20bc2 --- /dev/null +++ b/examples/01-basic/inline-content-objects/vite.config.ts @@ -0,0 +1,32 @@ +// AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY +import react from "@vitejs/plugin-react"; +import * as fs from "fs"; +import * as path from "path"; +import { defineConfig } from "vite"; +// import eslintPlugin from "vite-plugin-eslint"; +// https://vitejs.dev/config/ +export default defineConfig((conf) => ({ + plugins: [react()], + optimizeDeps: {}, + build: { + sourcemap: true, + }, + resolve: { + alias: + conf.command === "build" || + !fs.existsSync(path.resolve(__dirname, "../../packages/core/src")) + ? {} + : ({ + // Comment out the lines below to load a built version of blocknote + // or, keep as is to load live from sources with live reload working + "@blocknote/core": path.resolve( + __dirname, + "../../packages/core/src/" + ), + "@blocknote/react": path.resolve( + __dirname, + "../../packages/react/src/" + ), + } as any), + }, +})); diff --git a/playground/src/examples.gen.tsx b/playground/src/examples.gen.tsx index 30b425ae8f..2418e33558 100644 --- a/playground/src/examples.gen.tsx +++ b/playground/src/examples.gen.tsx @@ -89,6 +89,48 @@ "pathFromRoot": "examples/01-basic", "slug": "basic" } + }, + { + "projectSlug": "all-blocks", + "fullSlug": "basic/all-blocks", + "pathFromRoot": "examples/01-basic/all-blocks", + "config": { + "playground": true, + "docs": true + }, + "title": "All Block Types", + "group": { + "pathFromRoot": "examples/01-basic", + "slug": "basic" + } + }, + { + "projectSlug": "all-inline-content", + "fullSlug": "basic/all-inline-content", + "pathFromRoot": "examples/01-basic/all-inline-content", + "config": { + "playground": true, + "docs": true + }, + "title": "All Inline Content Types", + "group": { + "pathFromRoot": "examples/01-basic", + "slug": "basic" + } + }, + { + "projectSlug": "inline-content-objects", + "fullSlug": "basic/inline-content-objects", + "pathFromRoot": "examples/01-basic/inline-content-objects", + "config": { + "playground": true, + "docs": true + }, + "title": "Displaying Inline Content Objects", + "group": { + "pathFromRoot": "examples/01-basic", + "slug": "basic" + } } ] }, From 22c0a7f0bd791da07d8bf9dab85699b3b17d5206 Mon Sep 17 00:00:00 2001 From: yousefed Date: Mon, 26 Feb 2024 16:42:36 +0100 Subject: [PATCH 7/9] fix? --- docs/api/og.tsx | 2 +- docs/{public => assets}/fonts/Gilroy-Bold.ttf | Bin docs/{public => assets}/fonts/Gilroy-ExtraBold.ttf | Bin docs/{public => assets}/fonts/Gilroy-Medium.ttf | Bin docs/{public => assets}/fonts/Gilroy-Regular.ttf | Bin docs/{public => assets}/fonts/Gilroy-SemiBold.ttf | Bin docs/{public => assets}/fonts/gilroy-bold.woff2 | Bin .../{public => assets}/fonts/gilroy-extrabold.woff2 | Bin docs/{public => assets}/fonts/gilroy-medium.woff2 | Bin docs/{public => assets}/fonts/gilroy-regular.woff2 | Bin docs/{public => assets}/fonts/gilroy-semibold.woff2 | Bin 11 files changed, 1 insertion(+), 1 deletion(-) rename docs/{public => assets}/fonts/Gilroy-Bold.ttf (100%) rename docs/{public => assets}/fonts/Gilroy-ExtraBold.ttf (100%) rename docs/{public => assets}/fonts/Gilroy-Medium.ttf (100%) rename docs/{public => assets}/fonts/Gilroy-Regular.ttf (100%) rename docs/{public => assets}/fonts/Gilroy-SemiBold.ttf (100%) rename docs/{public => assets}/fonts/gilroy-bold.woff2 (100%) rename docs/{public => assets}/fonts/gilroy-extrabold.woff2 (100%) rename docs/{public => assets}/fonts/gilroy-medium.woff2 (100%) rename docs/{public => assets}/fonts/gilroy-regular.woff2 (100%) rename docs/{public => assets}/fonts/gilroy-semibold.woff2 (100%) diff --git a/docs/api/og.tsx b/docs/api/og.tsx index 96e4514e02..dd02f0b647 100644 --- a/docs/api/og.tsx +++ b/docs/api/og.tsx @@ -5,7 +5,7 @@ export const config = { }; const font = fetch( - new URL("../public/fonts/Gilroy-Regular.ttf", import.meta.url), + new URL("../../assets/fonts/Gilroy-Regular.ttf", import.meta.url), ).then((res) => res.arrayBuffer()); export default async function handler(request: { url: string }) { diff --git a/docs/public/fonts/Gilroy-Bold.ttf b/docs/assets/fonts/Gilroy-Bold.ttf similarity index 100% rename from docs/public/fonts/Gilroy-Bold.ttf rename to docs/assets/fonts/Gilroy-Bold.ttf diff --git a/docs/public/fonts/Gilroy-ExtraBold.ttf b/docs/assets/fonts/Gilroy-ExtraBold.ttf similarity index 100% rename from docs/public/fonts/Gilroy-ExtraBold.ttf rename to docs/assets/fonts/Gilroy-ExtraBold.ttf diff --git a/docs/public/fonts/Gilroy-Medium.ttf b/docs/assets/fonts/Gilroy-Medium.ttf similarity index 100% rename from docs/public/fonts/Gilroy-Medium.ttf rename to docs/assets/fonts/Gilroy-Medium.ttf diff --git a/docs/public/fonts/Gilroy-Regular.ttf b/docs/assets/fonts/Gilroy-Regular.ttf similarity index 100% rename from docs/public/fonts/Gilroy-Regular.ttf rename to docs/assets/fonts/Gilroy-Regular.ttf diff --git a/docs/public/fonts/Gilroy-SemiBold.ttf b/docs/assets/fonts/Gilroy-SemiBold.ttf similarity index 100% rename from docs/public/fonts/Gilroy-SemiBold.ttf rename to docs/assets/fonts/Gilroy-SemiBold.ttf diff --git a/docs/public/fonts/gilroy-bold.woff2 b/docs/assets/fonts/gilroy-bold.woff2 similarity index 100% rename from docs/public/fonts/gilroy-bold.woff2 rename to docs/assets/fonts/gilroy-bold.woff2 diff --git a/docs/public/fonts/gilroy-extrabold.woff2 b/docs/assets/fonts/gilroy-extrabold.woff2 similarity index 100% rename from docs/public/fonts/gilroy-extrabold.woff2 rename to docs/assets/fonts/gilroy-extrabold.woff2 diff --git a/docs/public/fonts/gilroy-medium.woff2 b/docs/assets/fonts/gilroy-medium.woff2 similarity index 100% rename from docs/public/fonts/gilroy-medium.woff2 rename to docs/assets/fonts/gilroy-medium.woff2 diff --git a/docs/public/fonts/gilroy-regular.woff2 b/docs/assets/fonts/gilroy-regular.woff2 similarity index 100% rename from docs/public/fonts/gilroy-regular.woff2 rename to docs/assets/fonts/gilroy-regular.woff2 diff --git a/docs/public/fonts/gilroy-semibold.woff2 b/docs/assets/fonts/gilroy-semibold.woff2 similarity index 100% rename from docs/public/fonts/gilroy-semibold.woff2 rename to docs/assets/fonts/gilroy-semibold.woff2 From ad799b35cdfec7b11fc238e28896a7a5758e7352 Mon Sep 17 00:00:00 2001 From: yousefed Date: Mon, 26 Feb 2024 16:49:58 +0100 Subject: [PATCH 8/9] disable og --- docs/api/{og.tsx => og.tsx.bak} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/api/{og.tsx => og.tsx.bak} (100%) diff --git a/docs/api/og.tsx b/docs/api/og.tsx.bak similarity index 100% rename from docs/api/og.tsx rename to docs/api/og.tsx.bak From 43e9796842b109adba77ad0241ece9b28ad262df Mon Sep 17 00:00:00 2001 From: Yousef Date: Tue, 27 Feb 2024 20:07:27 +0100 Subject: [PATCH 9/9] docs review (#596) * docs review * Fixed PR feedback --------- Co-authored-by: Matthew Lipski --- docs/pages/docs/advanced/vanilla-js.mdx | 110 +++++++------- docs/pages/docs/custom-schemas.mdx | 2 +- .../docs/custom-schemas/custom-styles.mdx | 21 +-- docs/pages/docs/editor-api.mdx | 10 +- .../docs/editor-api/converting-blocks.mdx | 6 +- .../docs/editor-api/manipulating-blocks.mdx | 47 +++--- .../manipulating-inline-content.mdx | 2 +- docs/pages/docs/editor-basics.mdx | 5 + docs/pages/docs/editor-basics/_meta.json | 6 +- .../docs/editor-basics/content-structure.mdx | 134 ---------------- ...t-content-types.mdx => default-schema.mdx} | 74 ++------- .../docs/editor-basics/document-structure.mdx | 103 +++++++++++++ docs/pages/docs/editor-basics/editor.mdx | 143 ------------------ docs/pages/docs/editor-basics/setup.mdx | 135 +++++++++++++++++ docs/pages/docs/glossary.mdx | 18 --- docs/pages/docs/index.mdx | 22 ++- docs/pages/docs/quickstart.mdx | 35 +---- docs/pages/docs/styling-theming.mdx | 4 +- .../docs/styling-theming/overriding-css.mdx | 22 ++- docs/pages/docs/styling-theming/themes.mdx | 44 +++--- docs/pages/docs/ui-components.mdx | 43 +----- .../docs/ui-components/suggestion-menus.mdx | 4 +- examples/01-basic/02-block-objects/App.tsx | 6 +- .../01-basic/03-block-manipulation/App.tsx | 8 +- examples/01-basic/04-saving-loading/App.tsx | 2 +- .../01-basic/inline-content-objects/App.tsx | 2 +- .../suggestion-menus-slash-menu-items/App.tsx | 1 - examples/05-custom-schema/font-style/App.tsx | 22 +-- .../01-converting-blocks-to-html/App.tsx | 2 +- .../02-converting-blocks-from-html/App.tsx | 4 +- .../03-converting-blocks-to-md/App.tsx | 2 +- .../04-converting-blocks-from-md/App.tsx | 4 +- .../blockManipulation.test.ts | 28 ++-- .../src/api/parsers/html/parseHTML.test.ts | 2 +- packages/core/src/editor/BlockNoteEditor.ts | 15 +- .../customblocks/TableOfContents.tsx.bak | 2 +- 36 files changed, 481 insertions(+), 609 deletions(-) create mode 100644 docs/pages/docs/editor-basics.mdx delete mode 100644 docs/pages/docs/editor-basics/content-structure.mdx rename docs/pages/docs/editor-basics/{default-content-types.mdx => default-schema.mdx} (53%) create mode 100644 docs/pages/docs/editor-basics/document-structure.mdx delete mode 100644 docs/pages/docs/editor-basics/editor.mdx create mode 100644 docs/pages/docs/editor-basics/setup.mdx delete mode 100644 docs/pages/docs/glossary.mdx diff --git a/docs/pages/docs/advanced/vanilla-js.mdx b/docs/pages/docs/advanced/vanilla-js.mdx index 518ed18903..0921d07b11 100644 --- a/docs/pages/docs/advanced/vanilla-js.mdx +++ b/docs/pages/docs/advanced/vanilla-js.mdx @@ -12,7 +12,10 @@ import { Callout } from "nextra/components"; BlockNote is mainly designed as a quick and easy drop-in block-based editor for React apps, but can also be used in vanilla JavaScript apps. However, this does involve writing your own UI elements. - We recommend using BlockNote with React so you can use the built-in UI components. This document will explain how you can use BlockNote without React, and write your own components, but this is not recommended as you'll lose the great out-of-the-box experience that BlockNote offers. + We recommend using BlockNote with React so you can use the built-in UI + components. This document will explain how you can use BlockNote without + React, and write your own components, but this is not recommended as you'll + lose the great out-of-the-box experience that BlockNote offers. ## Installing with NPM @@ -25,19 +28,14 @@ npm install @blocknote/core ## Creating an editor -TODO: AFAIK there is actually just no way of doing this anymore - This is how to create a new BlockNote editor: -``` typescript +```typescript import { BlockNoteEditor } from "@blocknote/core"; -const editor = BlockNoteEditor.create({ - element: document.getElementById("root")!, // element to append the editor to - onEditorContentChange: ({) => { - console.log(editor.getJSON()); - } -}); +const editor = BlockNoteEditor.create(); + +editor.mount(document.getElementById("root")); // element to append the editor to ``` Now, you'll have a plain BlockNote instance on your page. However, it's missing some menus and other UI elements. @@ -70,60 +68,58 @@ Let's look at how you could add the [Side Menu]() to your editor: import { BlockNoteEditor } from "@blocknote/core"; const editor = BlockNoteEditor.create({ - element: document.getElementById("root")! + element: document.getElementById("root")!, }); export function createButton(text: string, onClick?: () => void) { - const element = document.createElement("a"); - element.href = "#"; - element.text = text; - element.style.margin = "10px"; - - if (onClick) { - element.addEventListener("click", (e) => { - onClick(); - e.preventDefault(); - }); - } - - return element; + const element = document.createElement("a"); + element.href = "#"; + element.text = text; + element.style.margin = "10px"; + + if (onClick) { + element.addEventListener("click", (e) => { + onClick(); + e.preventDefault(); + }); + } + + return element; } let element: HTMLElement; editor.sideMenu.onUpdate((sideMenuState) => { - if (!element) { - element = document.createElement("div"); - element.style.background = "gray"; - element.style.position = "absolute"; - element.style.padding = "10px"; - element.style.opacity = "0.8"; - const addBtn = createButton("+", () => { - editor.sideMenu.addBlock(); - }); - element.appendChild(addBtn); - - const dragBtn = createButton("::", () => {}); - - dragBtn.addEventListener("dragstart", editor.sideMenu.blockDragStart); - dragBtn.addEventListener("dragend", editor.sideMenu.blockDragEnd); - dragBtn.draggable = true; - element.style.display = "none"; - element.appendChild(dragBtn); - - document.getElementById("root")!.appendChild(element); - } - - if (sideMenuState.show) { - element.style.display = "block"; - - element.style.top = sideMenuState.referencePos.top + "px"; - element.style.left = - sideMenuState.referencePos.x - element.offsetWidth + "px"; - } else { - element.style.display = "none"; - } + if (!element) { + element = document.createElement("div"); + element.style.background = "gray"; + element.style.position = "absolute"; + element.style.padding = "10px"; + element.style.opacity = "0.8"; + const addBtn = createButton("+", () => { + editor.sideMenu.addBlock(); + }); + element.appendChild(addBtn); + + const dragBtn = createButton("::", () => {}); + + dragBtn.addEventListener("dragstart", editor.sideMenu.blockDragStart); + dragBtn.addEventListener("dragend", editor.sideMenu.blockDragEnd); + dragBtn.draggable = true; + element.style.display = "none"; + element.appendChild(dragBtn); + + document.getElementById("root")!.appendChild(element); + } + + if (sideMenuState.show) { + element.style.display = "block"; + + element.style.top = sideMenuState.referencePos.top + "px"; + element.style.left = + sideMenuState.referencePos.x - element.offsetWidth + "px"; + } else { + element.style.display = "none"; + } }); ``` - -TODO: Bring back vanilla example? diff --git a/docs/pages/docs/custom-schemas.mdx b/docs/pages/docs/custom-schemas.mdx index 700a40cdda..b83c34a1a6 100644 --- a/docs/pages/docs/custom-schemas.mdx +++ b/docs/pages/docs/custom-schemas.mdx @@ -5,7 +5,7 @@ description: Learn how to create custom schemas for your BlockNote editor # Custom Schemas (advanced) -By default, BlockNote documents support different kind of blocks, inline content and text styles. See xxxx. +By default, BlockNote documents support different kind of blocks, inline content and text styles (see [default schema](/docs/editor-basics/default-schema)). However, you can extend BlockNote and create custom schemas to support your own blocks, inline content and text styles. ## Custom Blocks diff --git a/docs/pages/docs/custom-schemas/custom-styles.mdx b/docs/pages/docs/custom-schemas/custom-styles.mdx index 7a2e79d55c..729d6415ce 100644 --- a/docs/pages/docs/custom-schemas/custom-styles.mdx +++ b/docs/pages/docs/custom-schemas/custom-styles.mdx @@ -20,19 +20,14 @@ function createReactStyleSpec( Let's look at our custom font style from the demo, and go over each field to explain how it works: ```typescript -const Mention = createReactStyleSpec( +export const Font = createReactStyleSpec( { - type: "mention", - propSchema: { - user: { - default: "Unknown", - }, - }, - content: "none", - } as const, + type: "font", + propSchema: "string", + }, { render: (props) => ( - ... + ), } ); @@ -57,7 +52,7 @@ Defines the identifier of the custom style. The `PropSchema` specifies whether the style can only be toggled (`"boolean"`), or whether it can take a string value (`"string"`). Having a string value is useful for e.g. setting a color on the style. -_In the font style demo, we set this to `"string"` so we can store the font name._ +_In the font style demo, we set this to `"string"` so we can store the font family._ #### Style Implementation (`ReactCustomStyleImplementation`) @@ -78,9 +73,9 @@ This is your React component which defines how your custom style should be rende `value:` The string value of the style, this is only available if your style config contains `propSchema: "string"`. -`contentRef:` A React `ref` you can use to mark which element in your style is editable. +`contentRef:` A React `ref` to mark the editable element. -_Note that since styles are applied to text, you must set `contentRef` somewhere in you component, and should also return an HTML inline element._ +_Note that in contrast to Custom Blocks and Inline Content, the `render` function of Custom Styles cannot access React Context or other state. They should be plain React functions analogous to the example._ ### Adding Custom Style to the Editor diff --git a/docs/pages/docs/editor-api.mdx b/docs/pages/docs/editor-api.mdx index b6fc4c620b..45cd273b96 100644 --- a/docs/pages/docs/editor-api.mdx +++ b/docs/pages/docs/editor-api.mdx @@ -1 +1,9 @@ -hello \ No newline at end of file +# Editor API + +BlockNote exposes an API to interact with the editor and its contents from code. +These methods are directly available on the `BlockNoteEditor` object you created when instantiating your editor. + +- [Manipulating Blocks](/docs/editor-api/manipulating-blocks.md) explains how to read / update Blocks in the document. +- [Manipulating Inline Content](/docs/editor-api/manipulating-inline-content.md) explains how to update / read data from selected text. +- [Cursors & Selections](/docs/editor-api/cursor-selections) explains methods related to cursor positions and selections. +- [Markdown & HTML](/docs/converting-blocks) explains how to convert the document to and from Markdown and HTML. diff --git a/docs/pages/docs/editor-api/converting-blocks.mdx b/docs/pages/docs/editor-api/converting-blocks.mdx index ce4fea298c..26f4b9fc95 100644 --- a/docs/pages/docs/editor-api/converting-blocks.mdx +++ b/docs/pages/docs/editor-api/converting-blocks.mdx @@ -10,15 +10,13 @@ import { Callout } from "nextra/components"; # Markdown & HTML -TODO: use hooks in examples -TODO: use examples with content - It's possible to export or import Blocks to and from Markdown and HTML. The functions to import/export to and from Markdown/HTML are considered "lossy"; some information might be dropped when you export Blocks to those formats. - To serialize Blocks to a non-lossy format (for example, to store the contents of the editor in your backend), simply export the built-in Block format using `JSON.stringify(editor.topLevelBlocks)`. + To serialize Blocks to a non-lossy format (for example, to store the contents of the editor in your backend), simply export the built-in Block format using `JSON.stringify(editor.document)`. + ## Markdown diff --git a/docs/pages/docs/editor-api/manipulating-blocks.mdx b/docs/pages/docs/editor-api/manipulating-blocks.mdx index 3f9dc69f65..24b0dd63fa 100644 --- a/docs/pages/docs/editor-api/manipulating-blocks.mdx +++ b/docs/pages/docs/editor-api/manipulating-blocks.mdx @@ -9,17 +9,17 @@ path: /docs/manipulating-blocks Below, we explain the methods on `editor` you can use to read Blocks from the editor, and how to create / remove / update Blocks: -- [topLevelBlocks](/docs/editor-api/manipulating-blocks#getting-all-top-level-blocks) -- [getBlock](/docs/editor-api/manipulating-blocks#getting-a-specific-block) -- [forEachBlock](/docs/editor-api/manipulating-blocks#traversing-all-blocks) -- [insertBlocks](/docs/editor-api/manipulating-blocks#inserting-new-blocks) -- [updateBlock](/docs/editor-api/manipulating-blocks#updating-blocks) -- [removeBlocks](/docs/editor-api/manipulating-blocks#removing-blocks) -- [replaceBlocks](/docs/editor-api/manipulating-blocks#replacing-blocks) -- [canNestBlock](/docs/editor-api/manipulating-blocks#nesting-blocks) -- [nestBlock](/docs/editor-api/manipulating-blocks#nesting-blocks) -- [canUnnestBlock](/docs/editor-api/manipulating-blocks#un-nesting-blocks) -- [unnestBlock](/docs/editor-api/manipulating-blocks#un-nesting-blocks) +- [`get document`](/docs/editor-api/manipulating-blocks#getting-the-document) +- [`getBlock`](/docs/editor-api/manipulating-blocks#getting-a-specific-block) +- [`forEachBlock`](/docs/editor-api/manipulating-blocks#traversing-all-blocks) +- [`insertBlocks`](/docs/editor-api/manipulating-blocks#inserting-new-blocks) +- [`updateBlock`](/docs/editor-api/manipulating-blocks#updating-blocks) +- [`removeBlocks`](/docs/editor-api/manipulating-blocks#removing-blocks) +- [`replaceBlocks`](/docs/editor-api/manipulating-blocks#replacing-blocks) +- [`canNestBlock`](/docs/editor-api/manipulating-blocks#nesting-blocks) +- [`nestBlock`](/docs/editor-api/manipulating-blocks#nesting-blocks) +- [`canUnnestBlock`](/docs/editor-api/manipulating-blocks#un-nesting-blocks) +- [`unnestBlock`](/docs/editor-api/manipulating-blocks#un-nesting-blocks) ## Common types @@ -27,7 +27,7 @@ Before we dive into the methods, let's discuss some common types used in paramet ### Block Identifiers -The methods to access, insert, update, remove, or replace blocks, can require a `BlockIdentifier` as reference to an existing block in the document. +The methods to access, insert, update, remove, or replace blocks, can require a `BlockIdentifier` as reference to an existing block in the document. This is either a `string` representing the block ID, or a `Block` object from which the ID is taken: ```typescript @@ -36,7 +36,7 @@ type BlockIdentifier = string | Block; ### Partial Blocks -When retrieving blocks from the editor, you always receive complete `Block` objects. +When retrieving blocks from the editor, you always receive complete `Block` objects. For updating or creating blocks, you don't need to pass all properties and you can use a `PartialBlock` type instead: ```typescript @@ -51,25 +51,24 @@ type PartialBlock = { `PartialBlock` objects are almost the same as regular `Block` objects, but with all members optional and partial `props`. This makes updating or creating simpler blocks much easier. We'll see this below. - ## Accessing Blocks There are a few different ways to retrieve Blocks from the editor: -### Getting All Top-Level Blocks +### Getting the Document -Retrieve a snapshot of all top-level (non-nested) blocks in the editor using the following call: +Retrieve a snapshot of the document (all top-level, non-nested blocks) in the editor using the following call: ```typescript -topLevelBlocks: Block[]; +document: Block[]; // Usage -const blocks = editor.topLevelBlocks; +const blocks = editor.document; ``` `returns:` The document; a snapshot of all top-level (non-nested) blocks in the editor. -We already used this for the [Editor Content in JSON](/docs/editor-basics/content-structure#editor-content-in-json) demo. +We already used this for the [Editor Content in JSON](/docs/editor-basics/content-structure#editor-content-in-json) demo. ### Getting a Specific Block @@ -129,7 +128,7 @@ editor.insertBlocks([{type: "paragraph", text: "Hello World"}], referenceBlock, `placement:` Whether the blocks should be inserted just before, just after, or nested inside the `referenceBlock`. Inserts the blocks at the start of the existing block's children if `"nested"` is used. -If a block's `id` is undefined, BlockNote generates one automatically. +If a block's `id` is undefined, BlockNote generates one automatically. The method throws an error if the reference block could not be found. @@ -151,7 +150,7 @@ editor.updateBlock(blockToUpdate, { type: "paragraph" }); `update:` A [partial blocks](/docs/editor-api/manipulating-blocks#partial-blocks) which defines how the existing block should be changed. -Since `blockToUpdate` is a `PartialBlock` object, some fields might not be defined. These undefined fields are kept as-is from the existing block. +Since `blockToUpdate` is a `PartialBlock` object, some fields might not be defined. These undefined fields are kept as-is from the existing block. Throws an error if the block to update could not be found. @@ -190,7 +189,7 @@ editor.replaceBlocks(blocksToRemove, blocksToInsert) `blocksToInsert:` An array of [partial blocks](/docs/editor-api/manipulating-blocks#partial-blocks) that the existing ones should be replaced with. -If the blocks that should be removed are not adjacent or are at different nesting levels, `blocksToInsert` will be inserted at the position of the first block in `blocksToRemove`. +If the blocks that should be removed are not adjacent or are at different nesting levels, `blocksToInsert` will be inserted at the position of the first block in `blocksToRemove`. Throws an error if any of the blocks to remove could not be found. @@ -200,7 +199,7 @@ BlockNote also provides functions to nest & un-nest the block containing the [Te ### Nesting Blocks -TODO: this API seems different as it doesn't require a block id. should we make it possible to pass an identifier? +{/* TODO: this API seems different as it doesn't require a block id. should we make it possible to pass an identifier? */} Use `canNestBlock` to check whether the block containing the [Text Cursor](/docs/editor-api/cursor-selections#text-cursor) can be nested (i.e. if there is a block above it at the same nesting level): @@ -238,4 +237,4 @@ unnestBlock(): void; // Usage editor.unnestBlock(); -``` \ No newline at end of file +``` diff --git a/docs/pages/docs/editor-api/manipulating-inline-content.mdx b/docs/pages/docs/editor-api/manipulating-inline-content.mdx index b0ead98c1d..0f90561e11 100644 --- a/docs/pages/docs/editor-api/manipulating-inline-content.mdx +++ b/docs/pages/docs/editor-api/manipulating-inline-content.mdx @@ -7,7 +7,7 @@ path: /docs/block-content # Manipulating Inline Content -While `InlineContent` objects are used to describe a block's content, they can be cumbersome to work with directly. Therefore, BlockNote exposes functions which make it easier to edit block contents. +`BlockNoteEditor` exposes a number of functions to interact with the currently selected content. ## Accessing Styles diff --git a/docs/pages/docs/editor-basics.mdx b/docs/pages/docs/editor-basics.mdx new file mode 100644 index 0000000000..08889d9421 --- /dev/null +++ b/docs/pages/docs/editor-basics.mdx @@ -0,0 +1,5 @@ +# Editor basics + +In this section, we first explore the [methods to setup your editor](/docs/editor-basics/setup). +Then, we'll dive into the structure of documents, Blocks and rich text content in BlockNote ([document structure](/docs/editor-basics/document-structure)). +We'll also go over the blocks and content types that are part of BlockNote's [default built-in schema](/docs/editor-basics/default-schema). diff --git a/docs/pages/docs/editor-basics/_meta.json b/docs/pages/docs/editor-basics/_meta.json index 347454b190..56345dccb5 100644 --- a/docs/pages/docs/editor-basics/_meta.json +++ b/docs/pages/docs/editor-basics/_meta.json @@ -1,5 +1,5 @@ { - "content-structure": "Content Structure", - "default-content-types": "Default Content Types", - "editor": "Editor Setup" + "setup": "Editor Setup", + "document-structure": "Document Structure", + "default-schema": "Default Schema" } diff --git a/docs/pages/docs/editor-basics/content-structure.mdx b/docs/pages/docs/editor-basics/content-structure.mdx deleted file mode 100644 index 2b973f05c2..0000000000 --- a/docs/pages/docs/editor-basics/content-structure.mdx +++ /dev/null @@ -1,134 +0,0 @@ ---- -title: Content Structure -description: If you want to make the most out of BlockNote, it's important to understand how the editor's content is structured. -imageTitle: Content Structure ---- - -import { Example } from "@/components/example"; - -# Content Structure - -If you want to make the most out of BlockNote, it's important to understand how the editor's content is structured. - -## Blocks - -Each BlockNote editor is made up of a list of blocks. A block - like a heading, paragraph, or list item - contains a piece of content and optionally nested blocks: - -image - -### Block Objects - -In code, the `Block` type is used to describe any given block in the editor: - -```typescript -type Block = { - id: string; - type: string; - props: Record; - content: InlineContent[] | TableContent | undefined; - children: Block[]; -}; -``` - -`id:` The block's ID. Multiple blocks cannot share a single ID, and a block will keep the same ID from when it's created until it's removed. - -`type:` The block's type, such as a paragraph, heading, or list item. For an overview of built-in block types, see [Default Blocks](/docs/editor-basics/default-content-types#default-blocks). - -`props:` The block's properties, which is a set of key/value pairs that further specify how the block looks and behaves. Different block types have different props - see [Default Blocks](/docs/editor-basics/default-content-types#default-blocks) for more. - -`content:` The block's rich text content, usually represented as an array of `InlineContent` objects. This does not include content from any nested blocks. Read on to [Inline Content](/docs/editor-basics/content-structure#inline-content) for more on this. - -`children:` Any blocks nested inside the block. The nested blocks are also represented using `Block` objects. - -### Editor Content in JSON - -The demo below shows how the editor content is represented in code - notice that it's in JSON as an array of `Block` objects. - - - -## Inline Content - -A block's content is referred to as inline content, and is used to represent rich text. - -TODO: Between custom & default inline content types, the shapes on inline content objects are not standardized. -e.g. `Link`s have `href` and no props, custom IC will have props. `StyledText` can be IC itself, or used in other IC types. - -I think we should have smth like this: -```typescript -// Helper types -type Styles = { - bold: boolean; - textColor: string; - ... -} - -// Inline Content types -type StyledText = { - type: "richText"; - props: { - styles: Styles; - } - editable: true; -} -type Link = { - type: "link"; - props: { - styles: Styles; - href: string; - } - editable: true; -} -type Mention = { - type: "mention"; - props: { - user: string; - } - editable: false; -} -``` -This is way cleaner, but means that links cannot have mixed styles. I think we either: -- Convert links to styles (they're already marks in TipTap) -- Not do anything bc I don't think having mixed styles on links is ever useful - -### Inline Content Objects - -In code, the `InlineContent` type is used to describe a piece of inline content: - -```typescript -type InlineContent = { - type: string; - props: Record; - editable: boolean; -}; -``` - -`type:` The inline content's type, such as a mention or styled text. For an overview of built-in inline content types, see [Default Inline Content](/docs/editor-basics/default-content-types#default-inline-content). - -`props:` The inline content's properties, which are stored in a set of key/value pairs and specify how the inline content looks and behaves. Different inline content types have different props - see [Default Inline Content](/docs/editor-basics/default-content-types#default-inline-content) for more. - -`editable`: Whether the inline content contains editable text. - -### Types of Block Content - -Most blocks will use an array of `InlineContent` objects to describe their content, such as paragraphs, headings and list items. Some blocks, like [images](/docs/editor-basics/default-content-types#image), don't contain any rich text content, so their `content` fields will be `undefined`. - -[Tables](/docs/editor-basics/default-content-types#table) are also different, as they contain `TableContent`. Here, each table cell is represented as an array of `InlineContent` objects: - -```typescript -type TableContent = { - type: "tableContent"; - rows: { - cells: InlineContent[][]; - }[]; -}; -``` - -### Block Content in JSON - -The demo below shows how the block content is represented in code by outputting only the `content` field of each top level block. As in the [previous demo](/docs/editor-basics/content-structure#editor-content-in-json), notice that it's in JSON. - - \ No newline at end of file diff --git a/docs/pages/docs/editor-basics/default-content-types.mdx b/docs/pages/docs/editor-basics/default-schema.mdx similarity index 53% rename from docs/pages/docs/editor-basics/default-content-types.mdx rename to docs/pages/docs/editor-basics/default-schema.mdx index 2c0fae1bb5..4f2cebf656 100644 --- a/docs/pages/docs/editor-basics/default-content-types.mdx +++ b/docs/pages/docs/editor-basics/default-schema.mdx @@ -8,24 +8,20 @@ import { Example } from "@/components/example"; # Default Content Types -BlockNote supports a variety on built-in block and inline content types that are included in the editor by default. To create your own content types, see [Custom Schemas](/docs/custom-schemas). +BlockNote supports a number of built-in blocks, inline content types, and styles that are included in the editor by default. This is called the Default Schema. To create your own content types, see [Custom Schemas](/docs/custom-schemas). ## Default Blocks -BlockNote includes a number of built-in block types. The demo below contains each of them: +Quickly explore the default blocks in this demo: ### Reference -Here's an overview of all default blocks and the properties they support: +Let's look more in-depth at the default blocks and the properties they support: #### Paragraph -**Appearance** - -image - **Type & Props** ```typescript @@ -40,10 +36,6 @@ type ParagraphBlock = { #### Heading -**Appearance** - -image - **Type & Props** ```typescript @@ -62,10 +54,6 @@ type HeadingBlock = { #### Bullet List Item -**Appearance** - -image - **Type & Props** ```typescript @@ -80,10 +68,6 @@ type BulletListItemBlock = { #### Numbered List Item -**Appearance** - -image - **Type & Props** ```typescript @@ -98,10 +82,6 @@ type NumberedListItemBlock = { #### Image -**Appearance** - -image - **Type & Props** ```typescript @@ -126,10 +106,6 @@ type ImageBlock = { #### Table -**Appearance** - -image - **Type & Props** ```typescript @@ -144,7 +120,7 @@ type TableBlock = { ### Default Block Properties -There are some default block props that BlockNote uses for its default block types, and also exports for use in your own [custom blocks](/docs/custom-schemas/custom-blocks): +There are some default block props that BlockNote uses for the built-in blocks: ```typescript type DefaultProps = { @@ -160,13 +136,9 @@ type DefaultProps = { `textAlignment:` The text alignment of the block. -### Creating New Block Types - -Skip to [Custom Blocks](/docs/custom-schemas/custom-blocks) to learn how to do this. - ## Default Inline Content -BlockNote includes a number of built-in inline content types. The demo editor below displays all of them: +By default, `InlineContent` (the content of text blocks like paragraphs) in BlockNote can either be a `StyledText` or a `Link` object. Inspect them in the editor below: @@ -179,15 +151,6 @@ Here's an overview of all default inline content and the properties they support `StyledText` is a type of `InlineContent` used to display pieces of text with styles: ```typescript -type Styles = { - bold: true; - italic: true; - underline: true; - strikethrough: true; - textColor: string; - backgroundColor: string; -} - type StyledText = { type: "text"; text: string; @@ -207,31 +170,22 @@ type Link = { }; ``` -### Default Inline Content Properties - -TODO: This section only makes sense with the proposal in [Inline Content](/docs/editor-basics/content-structure#inline-content) +## Default Styles -There are some default inline content props that BlockNote uses for its default inline content types, and also exports for use in your own [custom inline content](/docs/custom-schemas/custom-inline-content): +The default text formatting options in BlockNote are represented by the `Styles` in the default schema: ```typescript type Styles = { - bold: true; - italic: true; - underline: true; - strikethrough: true; + bold: boolean; + italic: boolean; + underline: boolean; + strikethrough: boolean; textColor: string; backgroundColor: string; -} - -type DefaultInlineContentProps = { - styles: Styles }; ``` -`styles:` The text styles applied to the inline content. Only useful for editable inline content types. - -### Creating New Inline Content Types - -You can create your own custom inline content using React - skip to [Custom Inline Content](/docs/custom-schemas/custom-inline-content) to learn how to do this. +## Creating New Block or Inline Content Types -You can also create custom styles to apply to `StyledText` - skip to [Custom Styles](/docs/custom-schemas/custom-styles) to learn how to do this. \ No newline at end of file +You can also extend your editor and create your own Blocks, Inline Content or Styles using React. +Skip to [Custom Schemas (advanced)](/docs/custom-schemas) to learn how to do this. diff --git a/docs/pages/docs/editor-basics/document-structure.mdx b/docs/pages/docs/editor-basics/document-structure.mdx new file mode 100644 index 0000000000..1cc95a1682 --- /dev/null +++ b/docs/pages/docs/editor-basics/document-structure.mdx @@ -0,0 +1,103 @@ +--- +description: Learn how documents (the content of the rich text editor) are structured to make the most out of BlockNote. +--- + +import { Example } from "@/components/example"; + +# Document Structure + +Learn how documents (the content of the rich text editor) are structured to make the most out of BlockNote. + +## Blocks + +Each BlockNote document is made up of a list of blocks. +A block is a piece of content like a paragraph, heading, list item or image. Blocks can be dragged around by users in the editor. A block contains a piece of content and optionally nested (child) blocks: + +image + +### Block Objects + +The `Block` type is used to describe any given block in the editor: + +```typescript +type Block = { + id: string; + type: string; + props: Record; + content: InlineContent[] | TableContent | undefined; + children: Block[]; +}; +``` + +`id:` The block's ID. Multiple blocks cannot share a single ID, and a block will keep the same ID from when it's created until it's removed. + +`type:` The block's type, such as a paragraph, heading, or list item. For an overview of built-in block types, see [Default Blocks](/docs/editor-basics/default-content-types#default-blocks). + +`props:` The block's properties, which is a set of key/value pairs that further specify how the block looks and behaves. Different block types have different props - see [Default Blocks](/docs/editor-basics/default-content-types#default-blocks) for more. + +`content:` The block's rich text content, usually represented as an array of `InlineContent` objects. This does not include content from any nested blocks. Read on to [Inline Content](/docs/editor-basics/content-structure#inline-content) for more on this. + +`children:` Any blocks nested inside the block. The nested blocks are also represented using `Block` objects. + +### Editor Content in JSON + +The demo below shows the editor contents (document) in JSON. It's basically an array of `Block` objects that updates as you type in the editor: + + + +## Inline Content + +The `content` field of a block contains the rich-text content of a block. This is defined as an array of `InlineContent` objects. Inline content can either be styled text or a link (or a custom inline content type if you customize the editor schema). + +### Inline Content Objects + +The `InlineContent` type is used to describe a piece of inline content: + +```typescript +type Link = { + type: "link"; + content: StyledText[]; + href: string; +}; + +type StyledText = { + type: "text"; + text: string; + styles: Styles; +}; + +type InlineContent = Link | StyledText; +``` + +The `styles` property is explained below. + +### Other types of Block Content + +While most blocks use an array of `InlineContent` objects to describe their content (e.g.: paragraphs, headings, list items). Some blocks, like [images](/docs/editor-basics/default-content-types#image), don't contain any rich text content, so their `content` fields will be `undefined`. + +[Tables](/docs/editor-basics/default-content-types#table) are also different, as they contain `TableContent`. Here, each table cell is represented as an array of `InlineContent` objects: + +```typescript +type TableContent = { + type: "tableContent"; + rows: { + cells: InlineContent[][]; + }[]; +}; +``` + +## Styles and rich text + +The `styles` property of `StyledText` objects is used to describe the rich text styles (e.g.: bold, italic, color) or other attributes of a piece of text. It's a set of key / value pairs that specify the styles applied to the text. + +See the [Default Schema](/docs/editor-basics/default-schema) to learn which styles are included in BlockNote by default. + +## Demo: Block Content + +In the demo below, you can explore how the block content and styles are represented in JSON by outputting only the `content` field of each top level block. + + diff --git a/docs/pages/docs/editor-basics/editor.mdx b/docs/pages/docs/editor-basics/editor.mdx deleted file mode 100644 index b4080f12a1..0000000000 --- a/docs/pages/docs/editor-basics/editor.mdx +++ /dev/null @@ -1,143 +0,0 @@ ---- -title: Editor Setup -description: While BlockNote is ready to use out-of-the-box, there are a number of setup options you can choose to fit your use case. -imageTitle: Editor Setup -path: /docs/editor-basics/editor ---- - -import { Example } from "@/components/example"; - -TODO: -[ ] expose / document hooks -[ ] document events -[ ] explain controlled / uncontrolled - -# Editor Setup - -While BlockNote is ready to use out-of-the-box, there are a number of setup options you can choose to fit your use case. - -## Creating the Editor - -### Using a React Hook - -Most of the time, you'll want to create the BlockNote editor using the `useCreateBlockNote` hook. This will create a new editor when your React component gets mounted, and accepts a dependency array as with other React hooks. - -```ts -useCreateBlockNote = ( - options?: Partial, - deps?: React.DependencyList, -) => BlockNoteEditor; -``` - -Read on to [Options](/docs/editor-basics/editor#options) to see what options you can pass to the editor. - -### Using a Method - -You can also create a new editor using `BlockNoteEditor.create`. You should use this when you want to wait after your React component gets mounted before creating the editor. - -```ts -BlockNoteEditor.create = (options?: Partial) => - BlockNoteEditor; -``` - -In the demo below, we use `BlockNoteEditor.create` so we can fetch the editor's initial content from `localStorage` before creating it. - - - -Read on to [Options](/docs/editor-basics/editor#options) to see what options you can pass to the editor. - -### Options - -There are a number of options that you can pass to `useCreateBlockNote()` and `BlockNoteEditor.create`. You can find the full list of these below: - -```typescript -export type BlockNoteEditorOptions = Partial<{ - initialContent: PartialBlock[]; - domAttributes: Record; - slashMenuItems: ReactSlashMenuItem[]; - defaultStyles: boolean; - uploadFile: (file: File) => Promise; - collaboration: CollaborationOptions; - blockSpecs: BlockSpecs; - inlineContentSpecs: InlineContentSpecs; - styleSpecs: StyleSpecs; -}>; -``` - -`initialContent:` The content that should be in the editor when it's created, represented as an array of [partial block objects](/docs/manipulating-blocks#partial-blocks). - -`domAttributes:` An object containing HTML attributes that should be added to various DOM elements in the editor. See [Adding DOM Attributes](/docs/theming#adding-dom-attributes) for more. - -`slashMenuItems:` The commands that are listed in the editor's [Slash Menu](/docs/slash-menu). If this option isn't defined, a default list of commands is loaded. - -`defaultStyles`: Whether to use the default font and reset the styles of `

`, `

  • `, `

    `, etc. elements that are used in BlockNote. Defaults to true if undefined. - -`uploadFile`: A function which handles file uploads and eventually returns the URL to the uploaded file. Used by the [Image Toolbar](/docs/image-toolbar). - -`collaboration`: Options for enabling real-time collaboration. See [Collaboration](/docs/collaboration) for more info. - -`blockSpecs` (_advanced_): _advanced_ Specifications for Custom Blocks. See [Block Specs](/docs/block-specs) more info. - -`inlineContentSpecs` (_advanced_): Specifications for Custom Inline Content. See [Inline Content Specs](/docs/inline-content-specs) for more info. - -`styleSpecs` (_advanced_): Specifications for Custom Styles. See [Style Specs](/docs/style-specs) for more info. - -## Rendering the Editor - -### Using a React Component - -To, render the editor, you should use the `BlockNoteView` component, and pass in the editor created using `useCreateBlockNote` or `BlockNoteEditor.create`: - -```tsx -const editor = useCreateBlockNote(); - -return ; -``` - -### Props - -There are a number of additional props you can pass to `BlockNoteView`. You can find the full list of these below: - -```typescript -export type BlockNoteViewProps = Partial<{ - formattingToolbar?: boolean; - hyperlinkToolbar?: boolean; - sideMenu?: boolean; - slashMenu?: boolean; - imageToolbar?: boolean; - tableHandles?: boolean; - theme: - | "light" - | "dark" - | Theme - | { - light: Theme; - dark: Theme; - }; - editable?: boolean; - onSelectionChange?: () => void; - onChange?: () => void; -}>; -``` - -`formattingToolbar`: Whether the [Formatting Toolbar](/docs/ui-components/formatting-toolbar) should be enabled. - -`hyperlinkToolbar`: Whether the Hyperlink Toolbar should be enabled. - -`sideMenu`: Whether the [Block Side Menu](/docs/ui-components/side-menu) should be enabled. - -`slashMenu`: Whether the [Slash Menu](/docs/ui-components/suggestion-menus#slash-menu) should be enabled. - -`imageToolbar`: Whether the Image Toolbar should be enabled. - -`tableHandles`: Whether the Table Handles should be enabled. - -`theme`: The editor's theme, see [Themes](/docs/styling-theming/themes) for more about this. - -`editable`: Whether the editor should be editable. - -`onSelectionChange`: Callback for when the editor selection changes. - -`onChange`: Callback for when the editor selection or content changes. - -`BlockNoteView` also takes props that you can pass to any HTML `div` element. diff --git a/docs/pages/docs/editor-basics/setup.mdx b/docs/pages/docs/editor-basics/setup.mdx new file mode 100644 index 0000000000..05a926253d --- /dev/null +++ b/docs/pages/docs/editor-basics/setup.mdx @@ -0,0 +1,135 @@ +--- +description: Learn how to setup your BlockNote editor using the `useCreateBlockNote` hook and the ``BlockNoteView` component. +--- + +import { Example } from "@/components/example"; +import { Callout } from "nextra/components"; + +# Editor Setup + +You can customize your editor when you instantiate it. Let's take a closer looks at the basic methods and components to set up your BlockNote editor. + +## `useCreateBlockNote` hook + +Create a new `BlockNoteEditor` by calling the `useCreateBlockNote` hook. This instantiates a new editor and its required state. You can later interact with the editor using the Editor API and pass it to the `BlockNoteView` component. + +```ts +function useCreateBlockNote( + options?: BlockNoteEditorOptions, + deps?: React.DependencyList = [], +): BlockNoteEditor; + +type BlockNoteEditorOptions = { + initialContent?: PartialBlock[]; + domAttributes?: Record; + defaultStyles?: boolean; + uploadFile?: (file: File) => Promise; + collaboration?: CollaborationOptions; + schema?: BlockNoteSchema; +}; +``` + +The hook takes two optional parameters: + +**options:** An object containing options for the editor: + +`initialContent:` The content that should be in the editor when it's created, represented as an array of [partial block objects](/docs/manipulating-blocks#partial-blocks). + +`domAttributes:` An object containing HTML attributes that should be added to various DOM elements in the editor. See [Adding DOM Attributes](/docs/theming#adding-dom-attributes) for more. + +`defaultStyles`: Whether to use the default font and reset the styles of `

    `, `

  • `, `

    `, etc. elements that are used in BlockNote. Defaults to true if undefined. + +`uploadFile`: A function which handles file uploads and eventually returns the URL to the uploaded file. Used by the [Image Toolbar](/docs/image-toolbar). TODO + +`collaboration`: Options for enabling real-time collaboration. See [Collaboration](/docs/collaboration) for more info. + +`schema` (_advanced_): The editor schema if you want to extend your editor with custom blocks, styles, or inline content [Custom Schemas](/docs/custom-schemas). + +**deps:** Dependency array that's internally passed to `useMemo`. A new editor will only be created when this array changes. + + + Manually creating the editor (`BlockNoteEditor.create`) +

    + The `useCreateBlockNote` hook is actually a simple `useMemo` wrapper around + the `BlockNoteEditor.create` method. You can use this method directly if you + want to control the editor lifecycle manually. For example, we do this in + the [Saving & Loading example](/examples/basic/saving-loading) to delay the + editor creation until some content has been fetched from an external data + source. +

    +
    + +## Rendering the Editor with `` + +Use the `` component to render the `BlockNoteEditor` instance you just created: + +```tsx +const editor = useCreateBlockNote(); + +return ; +``` + +### Props + +There are a number of additional props you can pass to `BlockNoteView`. You can find the full list of these below: + +```typescript +export type BlockNoteViewProps = { + editor: BlockNoteEditor; + editable?: boolean; + onSelectionChange?: () => void; + onChange?: () => void; + theme?: + | "light" + | "dark" + | Theme + | { + light: Theme; + dark: Theme; + }; + formattingToolbar?: boolean; + hyperlinkToolbar?: boolean; + sideMenu?: boolean; + slashMenu?: boolean; + imageToolbar?: boolean; + tableHandles?: boolean; + children?: +} & HTMLAttributes; +``` + +`editor`: The `BlockNoteEditor` instance to render. + +`editable`: Whether the editor should be editable. + +`onSelectionChange`: Callback for when the editor selection changes. + +`onChange`: Callback for when the editor selection or content changes. + +`theme`: The editor's theme, see [Themes](/docs/styling-theming/themes) for more about this. + +`formattingToolbar`: Whether the [Formatting Toolbar](/docs/ui-components/formatting-toolbar) should be enabled. + +`hyperlinkToolbar`: Whether the Hyperlink Toolbar should be enabled. + +`sideMenu`: Whether the [Block Side Menu](/docs/ui-components/side-menu) should be enabled. + +`slashMenu`: Whether the [Slash Menu](/docs/ui-components/suggestion-menus#slash-menu) should be enabled. + +`imageToolbar`: Whether the Image Toolbar should be enabled. + +`tableHandles`: Whether the Table Handles should be enabled. + +`children`: Pass child elements to the `BlockNoteView` to create or customize toolbars, menus, or other UI components. See [UI Components](/docs/ui-components) for more. + +Additional props passed are forwarded to the HTML `div` element BlockNote renders internally. + + + Uncontrolled component +

    + Note that the `BlockNoteView` component is an [uncontrolled component](https://react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components). + This means you don't pass in the editor content directly as a prop. You can use the `initialContent` option in the `useCreateBlockNote` hook to set the initial content of the editor (similar to the `defaultValue` prop in a regular React `