chore: Adds a bus event to insert text at cursor in editor (#7968)
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
e5c198f839
commit
e27274a5a8
@@ -156,3 +156,62 @@ export function extractTextFromMarkdown(markdown) {
|
||||
.replace(/\n{2,}/g, '\n') // Remove multiple consecutive newlines (blank lines)
|
||||
.trim(); // Trim any extra space
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrolls the editor view into current cursor position
|
||||
*
|
||||
* @param {EditorView} view - The Prosemirror EditorView
|
||||
*
|
||||
*/
|
||||
export const scrollCursorIntoView = view => {
|
||||
// Get the current selection's head position (where the cursor is).
|
||||
const pos = view.state.selection.head;
|
||||
|
||||
// Get the corresponding DOM node for that position.
|
||||
const domAtPos = view.domAtPos(pos);
|
||||
const node = domAtPos.node;
|
||||
|
||||
// Scroll the node into view.
|
||||
if (node && node.scrollIntoView) {
|
||||
node.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a transaction that inserts a node into editor at the given position
|
||||
* Has an optional param 'content' to check if the
|
||||
*
|
||||
* @param {Node} node - The prosemirror node that needs to be inserted into the editor
|
||||
* @param {number} from - Position in the editor where the node needs to be inserted
|
||||
* @param {number} to - Position in the editor where the node needs to be replaced
|
||||
*
|
||||
*/
|
||||
export function insertAtCursor(editorView, node, from, to) {
|
||||
if (!editorView) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// This is a workaround to prevent inserting content into new line rather than on the exiting line
|
||||
// If the node is of type 'doc' and has only one child which is a paragraph,
|
||||
// then extract its inline content to be inserted as inline.
|
||||
const isWrappedInParagraph =
|
||||
node.type.name === 'doc' &&
|
||||
node.childCount === 1 &&
|
||||
node.firstChild.type.name === 'paragraph';
|
||||
|
||||
if (isWrappedInParagraph) {
|
||||
node = node.firstChild.content;
|
||||
}
|
||||
|
||||
let tr;
|
||||
if (to) {
|
||||
tr = editorView.state.tr.replaceWith(from, to, node).insertText(` `);
|
||||
} else {
|
||||
tr = editorView.state.tr.insert(from, node);
|
||||
}
|
||||
const state = editorView.state.apply(tr);
|
||||
editorView.updateState(state);
|
||||
editorView.focus();
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,41 @@ import {
|
||||
replaceSignature,
|
||||
cleanSignature,
|
||||
extractTextFromMarkdown,
|
||||
insertAtCursor,
|
||||
} from '../editorHelper';
|
||||
import { EditorState } from 'prosemirror-state';
|
||||
import { EditorView } from 'prosemirror-view';
|
||||
import { Schema } from 'prosemirror-model';
|
||||
|
||||
// Define a basic ProseMirror schema
|
||||
const schema = new Schema({
|
||||
nodes: {
|
||||
doc: { content: 'paragraph+' },
|
||||
paragraph: {
|
||||
content: 'text*',
|
||||
toDOM: () => ['p', 0], // Represents a paragraph as a <p> tag in the DOM.
|
||||
},
|
||||
text: {
|
||||
toDOM: node => node.text, // Represents text as its actual string value.
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Initialize a basic EditorState for testing
|
||||
const createEditorState = (content = '') => {
|
||||
if (!content) {
|
||||
return EditorState.create({
|
||||
schema,
|
||||
doc: schema.node('doc', null, [schema.node('paragraph')]),
|
||||
});
|
||||
}
|
||||
return EditorState.create({
|
||||
schema,
|
||||
doc: schema.node('doc', null, [
|
||||
schema.node('paragraph', null, [schema.text(content)]),
|
||||
]),
|
||||
});
|
||||
};
|
||||
|
||||
const NEW_SIGNATURE = 'This is a new signature';
|
||||
|
||||
@@ -198,3 +232,44 @@ describe('extractTextFromMarkdown', () => {
|
||||
expect(extractTextFromMarkdown(markdown)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('insertAtCursor', () => {
|
||||
it('should return undefined if editorView is not provided', () => {
|
||||
const result = insertAtCursor(undefined, schema.text('Hello'), 0);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should unwrap doc nodes that are wrapped in a paragraph', () => {
|
||||
const docNode = schema.node('doc', null, [
|
||||
schema.node('paragraph', null, [schema.text('Hello')]),
|
||||
]);
|
||||
|
||||
const editorState = createEditorState();
|
||||
const editorView = new EditorView(document.body, { state: editorState });
|
||||
|
||||
insertAtCursor(editorView, docNode, 0);
|
||||
|
||||
// Check if node was unwrapped and inserted correctly
|
||||
expect(editorView.state.doc.firstChild.firstChild.text).toBe('Hello');
|
||||
});
|
||||
|
||||
it('should insert node without replacing any content if "to" is not provided', () => {
|
||||
const editorState = createEditorState();
|
||||
const editorView = new EditorView(document.body, { state: editorState });
|
||||
|
||||
insertAtCursor(editorView, schema.text('Hello'), 0);
|
||||
|
||||
// Check if node was inserted correctly
|
||||
expect(editorView.state.doc.firstChild.firstChild.text).toBe('Hello');
|
||||
});
|
||||
|
||||
it('should replace content between "from" and "to" with the provided node', () => {
|
||||
const editorState = createEditorState('ReplaceMe');
|
||||
const editorView = new EditorView(document.body, { state: editorState });
|
||||
|
||||
insertAtCursor(editorView, schema.text('Hello'), 0, 8);
|
||||
|
||||
// Check if content was replaced correctly
|
||||
expect(editorView.state.doc.firstChild.firstChild.text).toBe('Hello Me');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user