From f95b13e93f04e650ab10c92c7ca180e6bb7b1456 Mon Sep 17 00:00:00 2001 From: Nick Perez Date: Fri, 25 Oct 2024 16:32:18 +0200 Subject: [PATCH] feat(core): allow nodes and fragments to be inserted into the editor using insertContentAt command #5764 (#5766) --- .../Commands/SetContent/React/index.spec.js | 21 ++++++++++++++++ packages/core/src/commands/insertContent.ts | 18 +++++++------- packages/core/src/commands/insertContentAt.ts | 12 +++++++++- packages/core/src/commands/setContent.ts | 24 ++++++++----------- packages/core/src/helpers/createDocument.ts | 6 +++-- .../core/src/helpers/createNodeFromContent.ts | 5 +++- 6 files changed, 59 insertions(+), 27 deletions(-) diff --git a/demos/src/Commands/SetContent/React/index.spec.js b/demos/src/Commands/SetContent/React/index.spec.js index f6fbdf20ea9..f2d9b03c7a2 100644 --- a/demos/src/Commands/SetContent/React/index.spec.js +++ b/demos/src/Commands/SetContent/React/index.spec.js @@ -14,6 +14,27 @@ context('/src/Commands/SetContent/React/', () => { }) }) + it('should insert raw JSON content', () => { + cy.get('.tiptap').then(([{ editor }]) => { + editor.commands.setContent({ type: 'paragraph', content: [{ type: 'text', text: 'Hello World.' }] }) + cy.get('.tiptap').should('contain.html', '

Hello World.

') + }) + }) + + it('should insert a Prosemirror Node as content', () => { + cy.get('.tiptap').then(([{ editor }]) => { + editor.commands.setContent(editor.schema.node('paragraph', null, editor.schema.text('Hello World.'))) + cy.get('.tiptap').should('contain.html', '

Hello World.

') + }) + }) + + it('should insert a Prosemirror Fragment as content', () => { + cy.get('.tiptap').then(([{ editor }]) => { + editor.commands.setContent(editor.schema.node('doc', null, editor.schema.node('paragraph', null, editor.schema.text('Hello World.'))).content) + cy.get('.tiptap').should('contain.html', '

Hello World.

') + }) + }) + it('should emit updates', () => { cy.get('.tiptap').then(([{ editor }]) => { let updateCount = 0 diff --git a/packages/core/src/commands/insertContent.ts b/packages/core/src/commands/insertContent.ts index 37e9bb14812..44210d69df1 100644 --- a/packages/core/src/commands/insertContent.ts +++ b/packages/core/src/commands/insertContent.ts @@ -1,4 +1,4 @@ -import { ParseOptions } from '@tiptap/pm/model' +import { Fragment, Node as ProseMirrorNode, ParseOptions } from '@tiptap/pm/model' import { Content, RawCommands } from '../types.js' @@ -14,7 +14,7 @@ declare module '@tiptap/core' { /** * The ProseMirror content to insert. */ - value: Content, + value: Content | ProseMirrorNode | Fragment, /** * Optional options @@ -23,17 +23,17 @@ declare module '@tiptap/core' { /** * Options for parsing the content. */ - parseOptions?: ParseOptions + parseOptions?: ParseOptions; /** * Whether to update the selection after inserting the content. */ - updateSelection?: boolean - applyInputRules?: boolean - applyPasteRules?: boolean - }, - ) => ReturnType - } + updateSelection?: boolean; + applyInputRules?: boolean; + applyPasteRules?: boolean; + } + ) => ReturnType; + }; } } diff --git a/packages/core/src/commands/insertContentAt.ts b/packages/core/src/commands/insertContentAt.ts index 520398ba1d8..33c84524d27 100644 --- a/packages/core/src/commands/insertContentAt.ts +++ b/packages/core/src/commands/insertContentAt.ts @@ -20,7 +20,7 @@ declare module '@tiptap/core' { /** * The ProseMirror content to insert. */ - value: Content, + value: Content | ProseMirrorNode | Fragment, /** * Optional options @@ -132,6 +132,16 @@ export const insertContentAt: RawCommands['insertContentAt'] = (position, value, // otherwise if it is an array, we have to join it if (Array.isArray(value)) { newContent = value.map(v => v.text || '').join('') + } else if (value instanceof Fragment) { + let text = '' + + value.forEach(node => { + if (node.text) { + text += node.text + } + }) + + newContent = text } else if (typeof value === 'object' && !!value && !!value.text) { newContent = value.text } else { diff --git a/packages/core/src/commands/setContent.ts b/packages/core/src/commands/setContent.ts index 0819c3cd05a..50211496b9d 100644 --- a/packages/core/src/commands/setContent.ts +++ b/packages/core/src/commands/setContent.ts @@ -1,4 +1,4 @@ -import { ParseOptions } from '@tiptap/pm/model' +import { Fragment, Node as ProseMirrorNode, ParseOptions } from '@tiptap/pm/model' import { createDocument } from '../helpers/createDocument.js' import { Content, RawCommands } from '../types.js' @@ -17,7 +17,7 @@ declare module '@tiptap/core' { /** * The new content. */ - content: Content, + content: Content | Fragment | ProseMirrorNode, /** * Whether to emit an update event. @@ -37,10 +37,10 @@ declare module '@tiptap/core' { /** * Whether to throw an error if the content is invalid. */ - errorOnInvalidContent?: boolean - }, - ) => ReturnType - } + errorOnInvalidContent?: boolean; + } + ) => ReturnType; + }; } } @@ -66,12 +66,8 @@ export const setContent: RawCommands['setContent'] = (content, emitUpdate = fals tr.setMeta('preventUpdate', !emitUpdate) } - return commands.insertContentAt( - { from: 0, to: doc.content.size }, - content, - { - parseOptions, - errorOnInvalidContent: options.errorOnInvalidContent ?? editor.options.enableContentCheck, - }, - ) + return commands.insertContentAt({ from: 0, to: doc.content.size }, content, { + parseOptions, + errorOnInvalidContent: options.errorOnInvalidContent ?? editor.options.enableContentCheck, + }) } diff --git a/packages/core/src/helpers/createDocument.ts b/packages/core/src/helpers/createDocument.ts index 44bcc669d4a..2ab6fcdd97d 100644 --- a/packages/core/src/helpers/createDocument.ts +++ b/packages/core/src/helpers/createDocument.ts @@ -1,4 +1,6 @@ -import { Node as ProseMirrorNode, ParseOptions, Schema } from '@tiptap/pm/model' +import { + Fragment, Node as ProseMirrorNode, ParseOptions, Schema, +} from '@tiptap/pm/model' import { Content } from '../types.js' import { createNodeFromContent } from './createNodeFromContent.js' @@ -11,7 +13,7 @@ import { createNodeFromContent } from './createNodeFromContent.js' * @returns The created Prosemirror document node */ export function createDocument( - content: Content, + content: Content | ProseMirrorNode | Fragment, schema: Schema, parseOptions: ParseOptions = {}, options: { errorOnInvalidContent?: boolean } = {}, diff --git a/packages/core/src/helpers/createNodeFromContent.ts b/packages/core/src/helpers/createNodeFromContent.ts index 63a9a2f9d67..5493b362c4a 100644 --- a/packages/core/src/helpers/createNodeFromContent.ts +++ b/packages/core/src/helpers/createNodeFromContent.ts @@ -23,10 +23,13 @@ export type CreateNodeFromContentOptions = { * @returns The created Prosemirror node or fragment */ export function createNodeFromContent( - content: Content, + content: Content | ProseMirrorNode | Fragment, schema: Schema, options?: CreateNodeFromContentOptions, ): ProseMirrorNode | Fragment { + if (content instanceof ProseMirrorNode || content instanceof Fragment) { + return content + } options = { slice: true, parseOptions: {},