UNPKG

@tiptap/core

Version:

headless rich text editor

98 lines (79 loc) 2.75 kB
import type { Node as ProseMirrorNode, Fragment, ResolvedPos } from '@tiptap/pm/model' import { createNodeFromContent } from '../helpers/createNodeFromContent.js' import { defaultBlockAt } from '../helpers/defaultBlockAt.js' import { selectionToInsertionEnd } from '../helpers/selectionToInsertionEnd.js' import type { Content, RawCommands } from '../types.js' export interface InsertDefaultBlockOptions { /** * The position to insert the block at. * Accepts a number or a resolved ProseMirror position. * Defaults to the current caret position. */ pos?: number | ResolvedPos /** * Attributes to apply to the inserted node. * Only attributes that match the node's schema will be applied. */ attrs?: Record<string, any> /** * Content to insert into the block. * Accepts string (plain text or HTML), ProseMirror node, or Fragment. */ content?: Content | ProseMirrorNode | Fragment /** * Whether to update the selection after inserting the content. * @default true */ updateSelection?: boolean } declare module '@tiptap/core' { interface Commands<ReturnType> { insertDefaultBlock: { /** * Insert a default content block at the given position. * Returns false when the default block can't be inserted at that position. * @example editor.commands.insertDefaultBlock() * @example editor.commands.insertDefaultBlock({ pos: 5, content: 'Hello' }) */ insertDefaultBlock: (options?: InsertDefaultBlockOptions) => ReturnType } } } export const insertDefaultBlock: RawCommands['insertDefaultBlock'] = (options = {}) => ({ tr, dispatch, editor }) => { const { pos, attrs, content, updateSelection = true } = options let $pos: ResolvedPos if (typeof pos === 'number') { $pos = tr.doc.resolve(pos) } else if (pos) { $pos = pos } else { $pos = tr.selection.$from } const defaultType = defaultBlockAt($pos.parent.contentMatchAt($pos.index())) if (!defaultType) { return false } const validAttrKeys = Object.keys(defaultType.spec.attrs || {}) const filteredAttrs = attrs ? Object.fromEntries(Object.entries(attrs).filter(([key]) => validAttrKeys.includes(key))) : {} let node: ProseMirrorNode | null | undefined if (content) { const parsed = createNodeFromContent(content, editor.schema) node = defaultType.createAndFill(filteredAttrs, parsed) } else { node = defaultType.createAndFill(filteredAttrs) } if (!node) { return false } if (dispatch) { tr.insert($pos.pos, node) if (updateSelection) { selectionToInsertionEnd(tr, tr.steps.length - 1, -1) } } return true }