Repository navigation
Container blocks: container flag, frames, keyboard settings, toggles #3059
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Open
Changes from all commits
Commits
Show all changes
38 commits
Select commit
Hold shift + click to select a range
e9bb1b2
refactor(core): simplify the BlockInfo API into the single vocabulary…
nperez0111 70fc8f6
refactor(core): consolidate BlockInfo shape and insertion helpers
nperez0111 db08d7d
feat(container-blocks): unify containers, owned children, and frames
nperez0111 df78829
test(container-blocks): cover editing, frames, repair, and serialization
nperez0111 05bf572
docs(container-blocks): document the API and add panel and callout ex…
nperez0111 c0626cf
refactor(core): simplify container UI and block DOM lookup
nperez0111 f13b2b7
refactor(container-blocks): simplify rendering, editing, and exports
nperez0111 afa6358
fix(core): normalize container parsing and export
nperez0111 56a63d0
fix(tests): streamline Docker build inputs and cache invalidation
nperez0111 0bce75a
fix(container-blocks): simplify editing and support plain text
nperez0111 1707571
refactor(container-blocks): simplify implementation and examples
nperez0111 32fe072
fix(core): complete frame lifecycle and preserve split children
nperez0111 4f3ade8
fix: address container block review feedback
nperez0111 c4f0431
fix(core): unwrap a dissolving container in place (#3104)
YousefED c3ac08a
test(core): add behaviour tests for the built-in toggle blocks
YousefED cffcf6d
refactor(core): draw the built-in toggles with renderFrame
YousefED 80f635a
feat(core)!: split keyboard behaviour from container structure
YousefED cd6bd89
feat(exporters): let block mappings place their children
YousefED 4714c87
fix(core): apply block colors to the children of framed blocks
YousefED 3bdd956
test(server-util): update the full HTML snapshot for block color attr…
YousefED 8f0d594
fix: address review findings on keyboard settings and toggles
YousefED 2e1f1ac
Merge branch 'main' into refactor/block-info-api
YousefED e206b7e
Merge branch 'refactor/block-info-api' into container-blocks/unified
YousefED 9e72dc7
docs(core): note that ignoreFrameChromeMutations needs tests or removal
YousefED 6caa68c
Merge branch 'container-blocks/unified' into container-blocks/editing…
YousefED 8bc7dbb
feat(core): drop a block into a toggle (BLO-956)
YousefED 2159763
fix(core): keep a block's id on Backspace below an alike empty block …
YousefED 0b93f12
fix(core): drop as usual when the drop target is gone
YousefED bfd0c28
test: update block spec snapshot for meta.dropsIntoChildren
YousefED cb0315f
fix: one source for the block types that the menus offer (BLO-990, BL…
YousefED 15714ef
refactor(core): rename the keyboard option to experimental_keyboard
YousefED 5a13195
Merge remote-tracking branch 'origin/container-blocks/editing-rules' …
YousefED b95f09f
Merge remote-tracking branch 'origin/main' into refactor/block-info-api
YousefED c9a9f26
Merge branch 'container-blocks/unified' into container-blocks/editing…
YousefED f2a4eb3
Merge branch 'refactor/block-info-api' into container-blocks/unified
YousefED 679148c
Merge branch 'container-blocks/editing-rules' into container-blocks/t…
YousefED 352329e
Fold #3142 and #3143 into #3059: keyboard settings, container flag, t…
YousefED 9778427
Merge main (#3051 squashed) into container-blocks/unified; content un…
YousefED File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
201 changes: 201 additions & 0 deletions
201
docs/content/docs/features/custom-schemas/container-blocks.mdx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,201 @@ | ||
| --- | ||
| title: Container Blocks | ||
| description: Learn how to create custom blocks that contain other blocks | ||
| --- | ||
|
|
||
| # Container Blocks | ||
|
|
||
| You can create custom blocks that contain other blocks, such as panels, callouts, and column layouts. Take a look at the demo below, in which we add a custom panel to a BlockNote editor, as well as a custom [Slash Menu item](/docs/react/components/suggestion-menus#changing-slash-menu-items) to insert it. Each panel can contain paragraphs, headings, lists, or any other blocks in the editor. | ||
|
|
||
| <Example name="custom-schema/container-block" /> | ||
|
|
||
| ## Creating a Container Block | ||
|
|
||
| Use the `createReactBlockSpec` function to create a container block, just like a [Custom Block](/docs/features/custom-schemas/custom-blocks). For the panel below, we set `content` to `"none"` and `container` to `true`, so the panel's child blocks go inside it: | ||
|
|
||
| ```tsx | ||
| import { createReactBlockSpec } from "@blocknote/react"; | ||
|
|
||
| export const createPanel = createReactBlockSpec( | ||
| { | ||
| type: "panel", | ||
| propSchema: {}, | ||
| content: "none", | ||
| container: true, | ||
| }, | ||
| { | ||
| render: (props) => ( | ||
| <div className="panel" ref={props.contentRef} /> | ||
| ), | ||
| }, | ||
| ); | ||
| ``` | ||
|
|
||
| ### Block Config | ||
|
|
||
| The block config defines the content and child blocks your container can hold: | ||
|
|
||
| `content:` Must be `"none"` for a container: its node holds nothing but its child blocks. For a block with its own text and child blocks, see [Combining Content and Child Blocks](#combining-content-and-child-blocks). | ||
|
|
||
| `container:` Set to `true` to put the block's child blocks inside it. Without it, a block's child blocks are indented below it. A container accepts any block by default. You can also restrict it to specific container types, as explained in [Restricting Children](#restricting-children). | ||
|
|
||
| `propSchema:` Defines the container's props, just like for other custom blocks. Use these to customize its appearance or behavior. | ||
|
|
||
| ### Block Implementation | ||
|
|
||
| `render:` Your React component defines how the block should look. For a container, attach `contentRef` where the child blocks should appear. With `content: "inline"` or `"plain"`, attach it to the block's own editable text. You can add icons, buttons, or other elements around it: | ||
|
|
||
| ```tsx | ||
| render: (props) => ( | ||
| <div className="panel"> | ||
| <span contentEditable={false}>💡</span> | ||
| <div ref={props.contentRef} /> | ||
| </div> | ||
| ), | ||
| ``` | ||
|
|
||
| You can style the component with CSS, just like any other React component: | ||
|
|
||
| ```css | ||
| .panel { | ||
| display: flex; | ||
| gap: 12px; | ||
| padding: 16px; | ||
| border-left: 4px solid #507aff; | ||
| border-radius: 6px; | ||
| } | ||
| ``` | ||
|
|
||
| ## Adding Container Blocks to the Editor | ||
|
|
||
| Add your container to a [BlockNote schema](/docs/features/custom-schemas#creating-your-own-schema): | ||
|
|
||
| ```typescript | ||
| import { BlockNoteSchema } from "@blocknote/core"; | ||
| import { createPanel } from "./Panel"; | ||
|
|
||
| const schema = BlockNoteSchema.create().extend({ | ||
| blockSpecs: { | ||
| panel: createPanel(), | ||
| }, | ||
| }); | ||
| ``` | ||
|
|
||
| You can then create an editor with this schema, as explained on the [Custom Schemas](/docs/features/custom-schemas) page. Use `children` to set the blocks inside a panel: | ||
|
|
||
| ```typescript | ||
| import { useCreateBlockNote } from "@blocknote/react"; | ||
|
|
||
| const editor = useCreateBlockNote({ | ||
| schema, | ||
| initialContent: [ | ||
| { | ||
| type: "panel", | ||
| children: [ | ||
| { type: "heading", content: "Getting started" }, | ||
| { type: "paragraph", content: "Follow these steps to get set up." }, | ||
| { type: "checkListItem", content: "Create an account" }, | ||
| ], | ||
| }, | ||
| ], | ||
| }); | ||
| ``` | ||
|
|
||
| If you create a panel without specifying its children, it starts with an empty paragraph. To let users insert panels themselves, add a [custom Slash Menu item](/docs/react/components/suggestion-menus#changing-slash-menu-items), as shown in the demo. | ||
|
|
||
| ## Combining Content and Child Blocks | ||
|
|
||
| A block can have both its own text and child blocks. Use this for a question followed by hints, a checklist item with detailed instructions, a code sample followed by explanatory blocks, or a callout with a heading. In the demo below, we use the block's text as a callout title: | ||
|
|
||
| <Example name="custom-schema/callout-block" /> | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's streamline the Alert + Callout examples (we now have an inconsistent mix I think) |
||
|
|
||
| Set `content` to `"inline"` for rich text or `"plain"` for unstyled text. Every block with content can have child blocks, so there's nothing to declare for them. Use `render` for the block's own text, `renderFrame` to style that content and its child blocks together, and `keyboard` to keep the child blocks inside the block: | ||
|
|
||
| ```tsx | ||
| import { createReactBlockSpec } from "@blocknote/react"; | ||
|
|
||
| export const createCallout = createReactBlockSpec( | ||
| { | ||
| type: "callout", | ||
| propSchema: {}, | ||
| content: "inline", | ||
| }, | ||
| { | ||
| experimental_keyboard: { | ||
| enter: "into-children", | ||
| childrenCanOutdent: false, | ||
| emptyChildEnter: "exit-at-end", | ||
| }, | ||
| render: (props) => ( | ||
| <div className="callout-title" ref={props.contentRef} /> | ||
| ), | ||
| renderFrame: (props) => ( | ||
| <div className="callout"> | ||
| <div ref={props.contentRef} /> | ||
| </div> | ||
| ), | ||
| }, | ||
| ); | ||
| ``` | ||
|
|
||
| `render:` Attach `contentRef` to the block's own editable text. With `content: "inline"`, users can format it and add links, just like in a paragraph. In this example, we style it as a callout title. | ||
|
|
||
| For plain text, set `content: "plain"` and use a `<pre>` element to display line breaks and spacing: | ||
|
|
||
| ```tsx | ||
| render: (props) => <pre ref={props.contentRef} />, | ||
| ``` | ||
|
|
||
| The child blocks can still contain rich text, images, and other block types. | ||
|
|
||
| `renderFrame:` An optional React component for styling the block and its children together, such as giving the callout a shared border or background. It receives `block`, `editor`, and `contentRef`, just like `render`. Attach `contentRef` where the block's content and children should appear. You can use the block's props to customize the frame, add interactive controls, or return `null` to show the block without a frame. | ||
|
|
||
| `experimental_keyboard:` Without it, child blocks behave like any indented blocks. This API is experimental and may change. Here, `enter: "into-children"` makes Enter in the title add a first child block, `childrenCanOutdent: false` keeps Shift-Tab from moving blocks out of the callout, and `emptyChildEnter: "exit-at-end"` makes Enter in an empty last block leave the callout. See [Custom Blocks](/docs/features/custom-schemas/custom-blocks) for all keyboard settings. | ||
|
|
||
| For a container with `content: "none"`, like the panel above, add the surrounding styling directly in `render`. | ||
|
|
||
| Add `callout: createCallout()` to your schema, then use `content` for the title and `children` for the blocks inside it: | ||
|
|
||
| ```typescript | ||
| { | ||
| type: "callout", | ||
| content: "Before you start", | ||
| children: [ | ||
| { type: "paragraph", content: "Make sure you have an account." }, | ||
| ], | ||
| } | ||
| ``` | ||
|
|
||
| Pressing Enter at the end of the title adds a paragraph inside the callout, and Enter in an empty last paragraph leaves the callout. Moving the callout moves its title and child blocks together. | ||
|
|
||
| To add blocks to an existing container, see [Inserting Blocks](/docs/reference/editor/manipulating-content#inserting-blocks). | ||
|
|
||
| ## Restricting Children | ||
|
|
||
| For structured layouts, you can limit a container to specific container types. For example, a column layout should only contain columns, while each column can contain any block. | ||
|
|
||
| Use an array of container type names for `children.allow`, and `min` to set the minimum number of children: | ||
|
|
||
| ```typescript | ||
| // Column layout config: | ||
| container: true, | ||
| children: { allow: ["column"], min: 2 }, | ||
| ``` | ||
|
|
||
| On the column itself, set `placeable` to `"namedOnly"` so it can only be used inside a container that explicitly allows it: | ||
|
|
||
| ```typescript | ||
| // Column config: | ||
| container: true, | ||
| placeable: "namedOnly", | ||
| ``` | ||
|
|
||
| `children.allow:` Accepts `"blocks"` (the default) or an array of container type names. You cannot list regular block types such as `"paragraph"` individually. | ||
|
|
||
| `children.min:` The minimum number of children. Defaults to `1`. | ||
|
|
||
| `placeable:` Set to `"namedOnly"` to restrict a container to parents that name it in `children.allow`. Defaults to `"anywhere"`. | ||
|
|
||
| These options need `container: true`. Other blocks can always have child blocks of any type, so for them `children` can only be the default, `{ allow: "blocks" }`. | ||
|
|
||
| For built-in column blocks, see [Multi-Column Layouts](/docs/foundations/document-structure#column-blocks). | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be
renderFrame?