mirror of
https://github.com/ueberdosis/tiptap.git
synced 2025-06-06 00:42:59 +08:00
Fix/insert content at block insertions (#5651)
* fix(core): dont split text nodes when insert block nodes at start of text * chore: added changeset * removed duplicated logic from horizontal rule
This commit is contained in:
parent
763af10c7b
commit
12bb31a099
5
.changeset/lazy-needles-train.md
Normal file
5
.changeset/lazy-needles-train.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"@tiptap/core": major
|
||||
---
|
||||
|
||||
`insertContent` and `insertContentAt` commands should not split text nodes like paragraphs into multiple nodes when the inserted content is at the beginning of the text to avoid empty nodes being created
|
@ -1,11 +1,12 @@
|
||||
import './styles.scss'
|
||||
|
||||
import { Color } from '@tiptap/extension-color'
|
||||
import { Image } from '@tiptap/extension-image'
|
||||
import ListItem from '@tiptap/extension-list-item'
|
||||
import TextStyle from '@tiptap/extension-text-style'
|
||||
import { EditorProvider, useCurrentEditor } from '@tiptap/react'
|
||||
import StarterKit from '@tiptap/starter-kit'
|
||||
import React from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
const htmlContent = `
|
||||
<h1><a href="https://tiptap.dev/">Tiptap</a></h1>
|
||||
@ -24,6 +25,12 @@ And more lines`
|
||||
const MenuBar = () => {
|
||||
const { editor } = useCurrentEditor()
|
||||
|
||||
const insertImage = useCallback(() => {
|
||||
const url = prompt('Enter an image URL')
|
||||
|
||||
editor.chain().insertContent(`<img src="${url}" alt="Example image" />`).focus().run()
|
||||
}, [editor])
|
||||
|
||||
if (!editor) {
|
||||
return null
|
||||
}
|
||||
@ -40,12 +47,14 @@ const MenuBar = () => {
|
||||
<button data-test-id="text-content" onClick={() => editor.chain().insertContent(textContent).focus().run()}>
|
||||
Insert text content
|
||||
</button>
|
||||
<button data-test-id="image-content" onClick={insertImage}>Insert image</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const extensions = [
|
||||
Image,
|
||||
Color.configure({ types: [TextStyle.name, ListItem.name] }),
|
||||
TextStyle.configure({ types: [ListItem.name] }),
|
||||
StarterKit.configure({
|
||||
|
@ -11,7 +11,7 @@ context('/src/Commands/InsertContent/React/', () => {
|
||||
cy.get('button[data-test-id="html-content"]').click()
|
||||
|
||||
// check if the content html is correct
|
||||
cy.get('.tiptap').should('contain.html', '<h1>Tiptap</h1><p><strong>Hello World</strong></p><p>This is a paragraph<br>with a break.</p><p>And this is some additional string content.</p>')
|
||||
cy.get('.tiptap').should('contain.html', '<h1><a target="_blank" rel="noopener noreferrer nofollow" href="https://tiptap.dev/">Tiptap</a></h1><p><strong>Hello World</strong></p><p>This is a paragraph<br>with a break.</p><p>And this is some additional string content.</p>')
|
||||
})
|
||||
|
||||
it('should keep spaces inbetween tags in html content', () => {
|
||||
@ -91,4 +91,21 @@ context('/src/Commands/InsertContent/React/', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('should split content when image is inserted inbetween text', () => {
|
||||
cy.get('.tiptap').then(([{ editor }]) => {
|
||||
editor.commands.insertContent('<p>HelloWorld</p>')
|
||||
editor.commands.setTextSelection(6)
|
||||
editor.commands.insertContent('<img src="https://example.image/1" alt="This is an example" />')
|
||||
cy.get('.tiptap').should('contain.html', '<p>Hello</p><img src="https://example.image/1" alt="This is an example" contenteditable="false" draggable="true"><p>World</p>')
|
||||
})
|
||||
})
|
||||
|
||||
it('should not split content when image is inserted at beginning of text', () => {
|
||||
cy.get('.tiptap').then(([{ editor }]) => {
|
||||
editor.commands.insertContent('<p>HelloWorld</p>')
|
||||
editor.commands.setTextSelection(1)
|
||||
editor.commands.insertContent('<img src="https://example.image/1" alt="This is an example" />')
|
||||
cy.get('.tiptap').should('contain.html', '<img src="https://example.image/1" alt="This is an example" contenteditable="false" draggable="true"><p>HelloWorld</p>')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
5864
package-lock.json
generated
5864
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -71,6 +71,7 @@ export const insertContentAt: RawCommands['insertContentAt'] = (position, value,
|
||||
}
|
||||
|
||||
let content: Fragment | ProseMirrorNode
|
||||
const { selection } = editor.state
|
||||
|
||||
try {
|
||||
content = createNodeFromContent(value, editor.schema, {
|
||||
@ -140,6 +141,13 @@ export const insertContentAt: RawCommands['insertContentAt'] = (position, value,
|
||||
} else {
|
||||
newContent = content
|
||||
|
||||
const fromSelectionAtStart = selection.$from.parentOffset === 0
|
||||
const isTextSelection = selection.$from.node().isText || selection.$from.node().isTextblock
|
||||
|
||||
if (fromSelectionAtStart && isTextSelection) {
|
||||
from = Math.max(0, from - 1)
|
||||
}
|
||||
|
||||
tr.replaceWith(from, to, newContent)
|
||||
}
|
||||
|
||||
|
@ -52,21 +52,11 @@ export const HorizontalRule = Node.create<HorizontalRuleOptions>({
|
||||
setHorizontalRule:
|
||||
() => ({ chain, state }) => {
|
||||
const { selection } = state
|
||||
const { $from: $originFrom, $to: $originTo } = selection
|
||||
const { $to: $originTo } = selection
|
||||
|
||||
const currentChain = chain()
|
||||
|
||||
if ($originFrom.parentOffset === 0) {
|
||||
currentChain.insertContentAt(
|
||||
{
|
||||
from: Math.max($originFrom.pos - 1, 0),
|
||||
to: $originTo.pos,
|
||||
},
|
||||
{
|
||||
type: this.name,
|
||||
},
|
||||
)
|
||||
} else if (isNodeSelection(selection)) {
|
||||
if (isNodeSelection(selection)) {
|
||||
currentChain.insertContentAt($originTo.pos, {
|
||||
type: this.name,
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user