Block
The base class of every block. A block wraps a node of the document tree (Loro) and holds the behaviour of one block type: the capability flags, the tree accessors, and the handlers for typing, keys, splitting, merging, pasting and serialization that a subclass overrides.
Related guide: Extensions →
Import
import { Block } from '@aionbuilders/nabu/extend';Constructor
new Block(nabu: Nabu, node: NabuNode)| Parameter | Type | Description |
|---|---|---|
nabu | Nabu | n/a |
node | NabuNode | n/a |
Static members
static fromHTML
static fromHTML: ((el: Element, helpers: HtmlHelpers, rule: HtmlRule, siblings: Element[]) => PasteBlock | PasteBlock[] | null) | undefinedMake the block(s) of an element matched by one of htmlRules; null / [] declines. siblings are the following
elements htmlSiblings claimed.
static fromMarkdown
static fromMarkdown: ((lines: string[], helpers: MarkdownHelpers, rule: MarkdownRule) => PasteBlock | PasteBlock[] | null) | undefinedMake the block(s) of the lines a markdownRules rule consumed; null declines (the next rule is tried).
static htmlRules
static htmlRules: HtmlRule[] | undefinedWhich HTML elements this block reads on paste (see HtmlRule). Declare it together with fromHTML.
static htmlSiblings
static htmlSiblings: ((el: Element, rule: HtmlRule, helpers: HtmlHelpers) => Element[]) | undefinedAsk to absorb the elements that follow el (a structure that spans several elements, a list in Google Docs); returns them in order.
static markdownRules
static markdownRules: MarkdownRule[] | undefinedHow this block starts and how many lines it takes in markdown (see MarkdownRule). Declare it together with fromMarkdown.
static toHtml
static toHtml: ((pb: PasteBlock, ctx: SerializeContext) => string) | undefinedThe HTML of a block of this type, for the clipboard's text/html.
static toMarkdown
static toMarkdown: ((pb: PasteBlock, ctx: SerializeContext) => string) | undefinedThe markdown of a block of this type, for the clipboard's text/plain and for containers that serialize their children.
static create()
create(
nabu: Nabu,
type: string,
props?: Record<string, any>,
parentId?: string | null,
index?: number | null
): Block| Parameter | Type | Default | Description |
|---|---|---|---|
nabu | Nabu | n/a | n/a |
type | string | n/a | n/a |
propsoptional | Record<string, any> | {} | n/a |
parentIdoptional | string | null | null | n/a |
indexoptional | number | null | null | n/a |
static load()
load(nabu: Nabu, node: NabuNode): Block| Parameter | Type | Description |
|---|---|---|
nabu | Nabu | n/a |
node | NabuNode | n/a |
Properties
behaviors
behaviors: SvelteMap<string, any>The behaviors of the block by name; a text block has 'text' (a TextBehavior).
component
component: Component<any, any, string> | nullelement
element: HTMLElement | nullid
id: `${number}@${number}`The Loro tree id of the node (also the key in nabu.blocks).
index
index: numberisIntermediate
isIntermediate: booleanThe selection covers this block and neither starts nor ends in it (reactive).
isolatesSelection
isolatesSelection: booleanWhen true, the text of this block is an ISOLATED FIELD (like a form input embedded in the document): a text selection
that does not lie entirely inside this block never touches its text. For such a selection (the engine's rules, see
container.utils.js, selection.svelte.js):
- range deletion, typing over it, cut and the marks skip the text of this block, and delete neither this block nor its container; the clipboard gets the TEXT blocks only (the block's own text and its container are not copied);
- two text blocks on either side of an isolated block, or in different "isolation owners" (the nearest ancestor that isolates), never merge, so a deletion across them clears both sides without joining them;
- the block is not highlighted (
data-nabu-isolated+ the root'sdata-nabu-range), and a selection end that would land in it is moved past it, in the direction of travel; a selection that STARTS in it stays bounded to it; - the caret enters it at its start going down / right, at its end going up / left; Select All inside it selects its text.
isSelectionEnd
isSelectionEnd: booleanThe selection ends in this block (reactive).
isSelectionStart
isSelectionStart: booleanThe selection starts in this block (reactive).
marksEnabled
marksEnabled: booleanWhen false, inline marks (bold/italic/...) are never applied to this block.
nabu
nabu: NabuThe editor this block belongs to.
next
next: Block | nullnode
node: NabuNodeThe block's node in the document tree (Loro): node.data holds its type and props, node.parent() / node.children() the structure.
parent
parent: MegaBlock | nullparents
parents: MegaBlock[]pastesAsPlainText
pastesAsPlainText: booleanWhen true, pasting inside this block inserts the clipboard's text/plain verbatim (replacing the selection), skipping every paste interpreter: no blocks created, no markdown/HTML interpretation.
placeholder
placeholder: stringResolved placeholder to render in data-placeholder ('' = none). Combines the
Nabu placeholders option, the block's emptiness and — with show: 'focus'
(default) — whether it holds the collapsed cursor.
The default text is declared by each block class as static placeholder
(string, false, or (block) => string | false), overridable per type through
the placeholders.text option. Blocks without a text behavior never show one.
(No static placeholder is declared here: Svelte forbids a static and a
reactive field with the same name in one class.)
positions
positions: numberprevious
previous: Block | nullselected
selected: booleanThe selection covers this block (reactive).
selection
selection: { direction: 'none' | 'forward' | 'backward'; from: number; to: number } | nullserializers
serializers: SvelteMap<string, (arg0: any) => any>Serializers of this block by format: 'markdown', 'json', 'application/x-nabu+json' (the clipboard) or one of your own.
Each receives the context of the call ({ from, to, partial } for the clipboard) and returns the serialized block.
splitsOnEnter
splitsOnEnter: booleanWhen false, Enter inserts a literal "\n" in place instead of splitting the block.
transformsEnabled
transformsEnabled: booleanWhen false, onBeforeInput markdown-shortcut hooks and inputRules are skipped in this block.
type
type: stringThe block type, as registered by its extension ('paragraph', 'callout'...). Read from the node when the block is built; retype() changes the node, and the engine builds a new block.
wordCount
wordCount: numberNumber of words contained in this block. Overridden by text blocks (own text) and containers (sum of children) so that parents can aggregate without re-traversing the whole subtree.
adoptsPastedTypes readonly
readonly adoptsPastedTypes: booleanWhether an EMPTY child of this block takes the type of a whole block pasted into it (a # Title pasted
into an empty paragraph makes it a heading). Always true for root blocks; a container with structural
rules about its children (a list) keeps it false, a container with a free-form body (a toggle) opts in.
hidesChildren readonly
readonly hidesChildren: booleanTrue when the block currently hides its children (they are not rendered, e.g. a collapsed
foldable block): navigation (findForward / findBackward / findLastDescendant, caret
normalisation) never enters them. Override with a getter in a subclass.
requiredParent readonly
readonly requiredParent: { props: () => Record<string, any>; type: string } | nullDeclares a required parent type for structural integrity. Override in subclasses (e.g. ListItem requires a "list" parent). Used by wrapOrphan() after block relocations.
Methods
absorbs()
absorbs(block: Block): any| Parameter | Type | Description |
|---|---|---|
block | Block | n/a |
acceptPaste()
acceptPaste(blocks: PasteBlock[], ctx: object): { block: Block | null; offset: number } | nullPaste extension point: lets the block that holds the caret decide how a pasted fragment lands, instead of the engine's default (first block merged into the caret block, the rest inserted after it as siblings). A block that lives in a container with its own rules about what it can hold overrides it (e.g. a list item turns pasted paragraphs into siblings items and splits its list around a block it cannot hold). The engine has already removed the selection; it commits the whole paste as ONE transaction (one undo step) and puts the caret at the returned point.
Contract: return null to DECLINE, and then touch nothing (the default insertion runs). Otherwise
apply the whole paste on the tree (the blocks are created through nabu.createPasteBlock /
nabu.insertPasteBlock), do NOT commit, and return where the caret goes.
| Parameter | Type | Description |
|---|---|---|
blocks | PasteBlock[] | the sanitized fragment |
ctx | object | the caret offset in this block's text |
ctx.offset | number | n/a |
Returns { block: Block | null; offset: number } | null
block: null leaves the caret to the engine
ascend()
ascend(eventName: string, event: Event | null, data?: Object): any| Parameter | Type | Default | Description |
|---|---|---|---|
eventName | string | n/a | n/a |
event | Event | null | n/a | n/a |
dataoptional | Object | {} | n/a |
beforeinput()
beforeinput(event: InputEvent): any| Parameter | Type | Description |
|---|---|---|
event | InputEvent | n/a |
clearSelection()
clearSelection(): voidcommit()
commit(): Promise<void>consume()
consume(otherBlock: Block, direction?: 'into' | 'from'): BlockConsumes another block, handling children relocation intelligently.
| Parameter | Type | Default | Description |
|---|---|---|---|
otherBlock | Block | n/a | The block to consume or be consumed by |
directionoptional | 'into' | 'from' | 'from' | 'into' = this merges into other, 'from' = other merges into this |
Returns Block
The surviving block
delete()
delete(deletion?: object): void| Parameter | Type | Description |
|---|---|---|
deletionoptional | object | n/a |
deletion.fromoptional | number | n/a |
deletion.indexoptional | number | n/a |
deletion.lengthoptional | number | n/a |
deletion.tooptional | number | n/a |
destroy()
destroy(): voiddragRange()
dragRange(): Block[]The blocks that travel together when THIS block is dragged by its handle (P-81): this block and anything the drag must carry with it, in document order, all in the same parent. The default is the block alone (a container carries its own children: they are its tree children). A block whose "extent" is not its subtree overrides it (an extension may widen the range). The drop rules are the same for any range: it cannot land inside itself.
findBackward()
findBackward(predicate: (block: Block) => boolean): Block | null| Parameter | Type | Description |
|---|---|---|
predicate | (block: Block) => boolean | n/a |
findForward()
findForward(predicate: (block: Block) => boolean): Block | nullThe next block in document order (own descendants first, then the following siblings with their descendants, then
those of each ancestor) that satisfies predicate, or null. A block that hides its children is not entered.
LINEAR: every block is visited at most once (the following siblings are only DESCENDED into, never searched
"forward" again, which would revisit everything after them).
| Parameter | Type | Description |
|---|---|---|
predicate | (block: Block) => boolean | n/a |
findLastDescendant()
findLastDescendant(predicate: (block: Block) => boolean): Block | nullHelper pour trouver le dernier descendant profond qui matche
| Parameter | Type | Description |
|---|---|---|
predicate | (block: Block) => boolean | n/a |
focus()
focus(
options?: { end?: number; offset?: number; start?: number } | null,
passive?: boolean
): { end: { node: Node; offset: number } | null; options: { endOffset: number; startOffset: number }; start: { node: Node; offset: number } | null }| Parameter | Type | Default | Description |
|---|---|---|---|
optionsoptional | { end?: number; offset?: number; start?: number } | null | {} | n/a |
passiveoptional | boolean | false | n/a |
getAdjacentSiblings()
getAdjacentSiblings(): {
next: null;
nextNode?: undefined;
previous: null;
previousNode?: undefined;
} | {
next: Block | null | undefined;
nextNode: LoroTreeNode<{ type: string } & Record<string, any>> | LoroTreeNode<Record<string, unknown>> | null;
previous: Block | null | undefined;
previousNode: LoroTreeNode<{ type: string } & Record<string, any>> | LoroTreeNode<Record<string, unknown>> | null;
}Returns real, uncommitted siblings, which is useful for extensions that want to check the document structure before the transaction is committed.
getDOMPoint()
getDOMPoint(offset: number): { node: Node; offset: number } | null| Parameter | Type | Description |
|---|---|---|
offset | number | n/a |
insert()
insert(index: number, text: string): void| Parameter | Type | Description |
|---|---|---|
index | number | n/a |
text | string | n/a |
keydown()
keydown(event: KeyboardEvent): any| Parameter | Type | Description |
|---|---|---|
event | KeyboardEvent | n/a |
mergeWith()
mergeWith(block: Block): any| Parameter | Type | Description |
|---|---|---|
block | Block | n/a |
resolveOffset()
resolveOffset(localOffset: number): { block: Block; offset: number } | nullResolve a document-local offset to { block, offset } within this block. Leaf blocks with text return themselves directly. Override in MegaBlock (pure containers) and mixed blocks (e.g. ListItem).
| Parameter | Type | Description |
|---|---|---|
localOffset | number | n/a |
retype()
retype(newType: string, props?: Record<string, any>): voidThe mutations of a type change, without the commit: the caller owns the transaction
(transformTo, the block:transform action…). By default the block is retyped IN PLACE (same node:
its id, text and position survive). Override it when a block cannot simply change type where it
stands (e.g. because its parent only accepts some children): do the same job with Loro mutations only.
| Parameter | Type | Default | Description |
|---|---|---|---|
newType | string | n/a | n/a |
propsoptional | Record<string, any> | {} | n/a |
serialize()
serialize(format: string, context?: Record<string, any>): any| Parameter | Type | Default | Description |
|---|---|---|---|
format | string | n/a | n/a |
contextoptional | Record<string, any> | {} | n/a |
split()
split(options: object): { block: Block } | null| Parameter | Type | Description |
|---|---|---|
options | object | n/a |
options.fromoptional | number | n/a |
options.indexoptional | number | n/a |
options.lengthoptional | number | n/a |
options.offsetoptional | number | n/a |
options.tooptional | number | n/a |
transformTo()
transformTo(newType: string, props?: Record<string, any>): voidTransforme ce bloc en un autre type de bloc.
| Parameter | Type | Default | Description |
|---|---|---|---|
newType | string | n/a | n/a |
propsoptional | Record<string, any> | {} | n/a |
Source: src/lib/blocks/block.svelte.js:32