bustle/mobiledoc-kit

Enter/Backspace with cursor at beginning of heading loses formatting

Opened this issue · 1 comments

If you place the cursor at the beginning of a heading and press Enter a new paragraph is created under the header with the heading text and no formatting, leaving you with a blank heading above. Similarly when pressing Backspace with a blank paragraph above the heading format is lost rather than the heading moved.

This is different to all other editors I've tested where (as expected IME) the heading is moved up/down rather than being split and formatting removed.

Mobiledoc:
enter-loses-header

Google docs:
google-docs-heading-enter

Dropbox Paper:
dropbox-paper-heading-enter

Medium:
medium-heading-enter

I've worked around this using the registerKeyCommand() hook, paraphrased example below.

import Editor from 'mobiledoc-kit/editor/editor';

export default Component.extend({
    willRender() {
        let editor = new Editor();

        editor.registerKeyCommand({
            str: 'Enter',
            run: run.bind(this, this.handleEnterKey, editor)
        });

        editor.registerKeyCommand({
            str: 'BACKSPACE',
            run: run.bind(this, this.handleBackspaceKey, editor)
        });
    }

    // if cursor is at beginning of a heading, insert a blank paragraph above
    handleEnterKey(editor) {
        let {isCollapsed, head: {offset, section}} = editor.range;

        if (isCollapsed && offset === 0 && section.tagName.match(/^h\d$/)) {
            editor.run((postEditor) => {
                let newPara = postEditor.builder.createMarkupSection('p');
                let collection = section.parent.sections;
                postEditor.insertSectionBefore(collection, newPara, section);
            });
            return;
        }

        return false;
    },

    // if cursor is at the beginning of a heading and previous section is a blank paragraph,
    // delete the blank paragraph
    handleBackspaceKey(editor) {
        let {isCollapsed, head: {offset, section}} = editor.range;

        if (isCollapsed && offset === 0 && section.tagName.match(/^h\d$/) && section.prev.tagName === 'p' && section.prev.isBlank) {
            editor.run((postEditor) => {
                postEditor.removeSection(section.prev);
            });
            return;
        }

        return false;
    }
});

I initially attempted to use the willHandleNewline(event) hook with event.preventDefault() but unfortunately that pushes two undo states onto the undo stack meaning you had to press Cmd+Z twice to undo a single carriage return when pushing a heading down the doc.