0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-24 23:48:13 -05:00

Converted <GhKoenigEditor> to glimmer component

no issue

- switched to Ember Octane patterns
  - use native class instead of EmberObject syntax
  - moved container element into template
This commit is contained in:
Kevin Ansfield 2021-05-27 17:59:22 +01:00
parent 5f582c0d16
commit 6d66c2cc73
2 changed files with 134 additions and 150 deletions

View file

@ -1,37 +1,40 @@
{{!-- full height content pane --}} <div class="gh-koenig-editor relative w-100 vh-100 overflow-x-hidden overflow-y-auto z-0" {{did-insert this.registerElement}} ...attributes>
<div {{!-- full height content pane --}}
class="gh-koenig-editor-pane flex flex-column mih-100" <div
onmousedown={{action "trackMousedown"}} class="gh-koenig-editor-pane flex flex-column mih-100"
onmouseup={{action "focusEditor"}} {{on "mousedown" this.trackMousedown}}
> {{on "mouseup" this.focusEditor}}
<GhTextarea >
@class="gh-editor-title" <GhTextarea
@placeholder={{this.titlePlaceholder}} @class="gh-editor-title"
@tabindex="1" @placeholder={{@titlePlaceholder}}
@autoExpand=".gh-koenig-editor" @tabindex="1"
@value={{readonly this.title}} @autoExpand=".gh-koenig-editor"
@input={{action "onTitleChange" value="target.value"}} @value={{readonly @title}}
@focus-out={{action "onTitleFocusOut"}} @input={{this.updateTitle}}
@keyDown={{action "onTitleKeydown"}} @focus-out={{optional @onTitleBlur}}
@didCreateTextarea={{action "onTitleCreated"}} data-test-editor-title-input={{true}} @keyDown={{this.onTitleKeydown}}
/> @didCreateTextarea={{this.registerTitleElement}}
data-test-editor-title-input={{true}}
/>
<KoenigEditor <KoenigEditor
@mobiledoc={{this.body}} @mobiledoc={{@body}}
@placeholder={{this.bodyPlaceholder}} @placeholder={{@bodyPlaceholder}}
@autofocus={{this.bodyAutofocus}} @autofocus={{or @bodyAutofocus false}}
@spellcheck={{true}} @spellcheck={{true}}
@onChange={{action "onBodyChange"}} @onChange={{@onBodyChange}}
@didCreateEditor={{action "onEditorCreated"}} @didCreateEditor={{this.onEditorCreated}}
@cursorDidExitAtTop={{action "focusTitle"}} @cursorDidExitAtTop={{this.focusTitle}}
@headerOffset={{this.headerOffset}} @headerOffset={{@headerOffset}}
@dropTargetSelector=".gh-koenig-editor-pane" @dropTargetSelector=".gh-koenig-editor-pane"
@scrollContainerSelector={{this.scrollContainerSelector}} @scrollContainerSelector={{@scrollContainerSelector}}
@scrollOffsetTopSelector={{this.scrollOffsetTopSelector}} @scrollOffsetTopSelector={{@scrollOffsetTopSelector}}
@scrollOffsetBottomSelector={{this.scrollOffsetBottomSelector}} @scrollOffsetBottomSelector={{@scrollOffsetBottomSelector}}
@wordCountDidChange={{action this.onWordCountChange}} @wordCountDidChange={{@onWordCountChange}}
@snippets={{@snippets}} @snippets={{@snippets}}
@saveSnippet={{@saveSnippet}} @saveSnippet={{@saveSnippet}}
@deleteSnippet={{@deleteSnippet}} @deleteSnippet={{@deleteSnippet}}
/> />
</div>
</div> </div>

View file

@ -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 @action
classNames: ['gh-koenig-editor', 'relative', 'w-100', 'vh-100', 'overflow-x-hidden', 'overflow-y-auto', 'z-0'], registerElement(element) {
title: '', this.containerElement = element;
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
trackMousedown(event) {
// triggered when a mousedown is registered on .gh-koenig-editor-pane // 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 // Title actions -----------------------------------------------------------
focusEditor(event) {
if (event.target.classList.contains('gh-koenig-editor-pane')) {
let editorCanvas = this._editor.element;
let {bottom} = editorCanvas.getBoundingClientRect();
// if a mousedown and subsequent mouseup occurs below the editor @action
// canvas, focus the editor and put the cursor at the end of the registerTitleElement(element) {
// document this.titleElement = element;
if (this._mousedownY > bottom && event.clientY > bottom) { }
let {post} = this._editor;
let range = post.toRange();
let {tailSection} = range;
event.preventDefault(); @action
this._editor.focus(); updateTitle(event) {
this.args.onTitleChange?.(event.target.value);
}
// we should always have a visible cursor when focusing @action
// at the bottom so create an empty paragraph if last focusTitle() {
// section is a card this.titleElement.focus();
if (tailSection.isCardSection) { }
this._editor.run((postEditor) => {
let newSection = postEditor.builder.createMarkupSection('p');
postEditor.insertSectionAtEnd(newSection);
tailSection = newSection;
});
}
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 // enter will always focus the editor
this.element.scrollTop = this.element.scrollHeight; // 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 -------------------------------------------- */ this.koenigEditor.focus();
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);
} }
}, }
/* 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) { _setupEditor(koenig) {
let component = this; let component = this;
this._koenig = koenig; this.koenigEditor = koenig.editor;
this._editor = koenig.editor;
// focus the title when pressing SHIFT+TAB // focus the title when pressing SHIFT+TAB
this._editor.registerKeyCommand({ this.koenigEditor.registerKeyCommand({
str: 'SHIFT+TAB', str: 'SHIFT+TAB',
run() { run() {
component.send('focusTitle'); component.focusTitle();
return true; return true;
} }
}); });
}, }
_addParaAtTop() { _addParaAtTop() {
if (!this._editor) { if (!this.koenigEditor) {
return; return;
} }
let editor = this._editor; let editor = this.koenigEditor;
let section = editor.post.toRange().head.section; let section = editor.post.toRange().head.section;
// create a blank paragraph at the top of the editor unless it's already // create a blank paragraph at the top of the editor unless it's already
@ -157,4 +138,4 @@ export default Component.extend({
}); });
} }
} }
}); }