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 `