diff --git a/ghost/admin/app/components/gh-koenig-editor.hbs b/ghost/admin/app/components/gh-koenig-editor.hbs index 076d71b058..a0bcd3b89f 100644 --- a/ghost/admin/app/components/gh-koenig-editor.hbs +++ b/ghost/admin/app/components/gh-koenig-editor.hbs @@ -1,37 +1,40 @@ -{{!-- full height content pane --}} -
- +
+ {{!-- full height content pane --}} +
+ - + +
\ No newline at end of file diff --git a/ghost/admin/app/components/gh-koenig-editor.js b/ghost/admin/app/components/gh-koenig-editor.js index 569267cc44..7e1f4e993c 100644 --- a/ghost/admin/app/components/gh-koenig-editor.js +++ b/ghost/admin/app/components/gh-koenig-editor.js @@ -1,148 +1,129 @@ -import Component from '@ember/component'; +import Component from '@glimmer/component'; +import {action} from '@ember/object'; -export default Component.extend({ +export default class GhKoenigEditorComponent extends Component { + containerElement = null; + titleElement = null; + koenigEditor = null; + mousedownY = 0; - // public attrs - classNames: ['gh-koenig-editor', 'relative', 'w-100', 'vh-100', 'overflow-x-hidden', 'overflow-y-auto', 'z-0'], - title: '', - titlePlaceholder: '', - body: null, - bodyPlaceholder: '', - bodyAutofocus: false, - - // internal properties - _title: null, - _editor: null, - _mousedownY: 0, - - // closure actions - onTitleChange() {}, - onTitleBlur() {}, - onBodyChange() {}, - onEditorCreated() {}, - onWordCountChange() {}, - - actions: { - focusTitle() { - this._title.focus(); - }, + @action + registerElement(element) { + this.containerElement = element; + } + @action + trackMousedown(event) { // triggered when a mousedown is registered on .gh-koenig-editor-pane - trackMousedown(event) { - this._mousedownY = event.clientY; - }, + this.mousedownY = event.clientY; + } - // triggered when a mouseup is registered on .gh-koenig-editor-pane - focusEditor(event) { - if (event.target.classList.contains('gh-koenig-editor-pane')) { - let editorCanvas = this._editor.element; - let {bottom} = editorCanvas.getBoundingClientRect(); + // Title actions ----------------------------------------------------------- - // if a mousedown and subsequent mouseup occurs below the editor - // canvas, focus the editor and put the cursor at the end of the - // document - if (this._mousedownY > bottom && event.clientY > bottom) { - let {post} = this._editor; - let range = post.toRange(); - let {tailSection} = range; + @action + registerTitleElement(element) { + this.titleElement = element; + } - event.preventDefault(); - this._editor.focus(); + @action + updateTitle(event) { + this.args.onTitleChange?.(event.target.value); + } - // we should always have a visible cursor when focusing - // at the bottom so create an empty paragraph if last - // section is a card - if (tailSection.isCardSection) { - this._editor.run((postEditor) => { - let newSection = postEditor.builder.createMarkupSection('p'); - postEditor.insertSectionAtEnd(newSection); - tailSection = newSection; - }); - } + @action + focusTitle() { + this.titleElement.focus(); + } - this._editor.selectRange(tailSection.tailPosition()); + @action + onTitleKeydown(event) { + let value = event.target.value; + let selectionStart = event.target.selectionStart; - // ensure we're scrolled to the bottom - this.element.scrollTop = this.element.scrollHeight; - } + // enter will always focus the editor + // down arrow will only focus the editor when the cursor is at the + // end of the input to preserve the default OS behaviour + if ( + event.key === 'Enter' || + event.key === 'Tab' || + ((event.key === 'ArrowDown' || event.key === 'ArrowRight') && (!value || selectionStart === value.length)) + ) { + event.preventDefault(); + + // on Enter we also want to create a blank para if necessary + if (event.key === 'Enter') { + this._addParaAtTop(); } - }, - /* title related actions -------------------------------------------- */ - - onTitleCreated(titleElement) { - this._title = titleElement; - }, - - onTitleChange(newTitle) { - this.onTitleChange(newTitle); - }, - - onTitleFocusOut() { - this.onTitleBlur(); - }, - - onTitleKeydown(event) { - let value = event.target.value; - let selectionStart = event.target.selectionStart; - - // enter will always focus the editor - // down arrow will only focus the editor when the cursor is at the - // end of the input to preserve the default OS behaviour - if ( - event.key === 'Enter' || - event.key === 'Tab' || - ((event.key === 'ArrowDown' || event.key === 'ArrowRight') && (!value || selectionStart === value.length)) - ) { - event.preventDefault(); - - // on Enter we also want to create a blank para if necessary - if (event.key === 'Enter') { - this._addParaAtTop(); - } - - this._editor.focus(); - } - }, - - /* body related actions --------------------------------------------- */ - - onEditorCreated(koenig) { - this._setupEditor(koenig); - this.onEditorCreated(koenig); - }, - - onBodyChange(newMobiledoc) { - this.onBodyChange(newMobiledoc); + this.koenigEditor.focus(); } - }, + } - /* public methods ------------------------------------------------------- */ + // Body actions ------------------------------------------------------------ - /* internal methods ----------------------------------------------------- */ + @action + onEditorCreated(koenig) { + this._setupEditor(koenig); + this.args.onEditorCreated?.(koenig); + } + + @action + focusEditor(event) { + if (event.target.classList.contains('gh-koenig-editor-pane')) { + let editorCanvas = this.koenigEditor.element; + let {bottom} = editorCanvas.getBoundingClientRect(); + + // if a mousedown and subsequent mouseup occurs below the editor + // canvas, focus the editor and put the cursor at the end of the + // document + if (this.mousedownY > bottom && event.clientY > bottom) { + let {post} = this.koenigEditor; + let range = post.toRange(); + let {tailSection} = range; + + event.preventDefault(); + this.koenigEditor.focus(); + + // we should always have a visible cursor when focusing + // at the bottom so create an empty paragraph if last + // section is a card + if (tailSection.isCardSection) { + this.koenigEditor.run((postEditor) => { + let newSection = postEditor.builder.createMarkupSection('p'); + postEditor.insertSectionAtEnd(newSection); + tailSection = newSection; + }); + } + + this.koenigEditor.selectRange(tailSection.tailPosition()); + + // ensure we're scrolled to the bottom + this.containerElement.scrollTop = this.containerElement.scrollHeight; + } + } + } _setupEditor(koenig) { let component = this; - this._koenig = koenig; - this._editor = koenig.editor; + this.koenigEditor = koenig.editor; // focus the title when pressing SHIFT+TAB - this._editor.registerKeyCommand({ + this.koenigEditor.registerKeyCommand({ str: 'SHIFT+TAB', run() { - component.send('focusTitle'); + component.focusTitle(); return true; } }); - }, + } _addParaAtTop() { - if (!this._editor) { + if (!this.koenigEditor) { return; } - let editor = this._editor; + let editor = this.koenigEditor; let section = editor.post.toRange().head.section; // create a blank paragraph at the top of the editor unless it's already @@ -157,4 +138,4 @@ export default Component.extend({ }); } } -}); +}