mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-25 09:03:12 +03:00
Duplicated <GhKoenigEditor>
to <GhKoenigEditorReact>
no issue - the component wraps the title and editor canvas components, we only want to replace the canvas component with a react experiment so we need a duplicate for use in the `react-editor` experiment templates
This commit is contained in:
parent
a76132bda3
commit
81f3718867
@ -939,3 +939,8 @@ add|ember-template-lint|no-action|4|11|4|11|831dd12209c22868d0614c65417281fda799
|
|||||||
add|ember-template-lint|require-valid-alt-text|3|44|3|44|a7f0566c430150bae4153e0dfb489a218bdeb8a4|1658102400000|1668474000000|1673658000000|lib/koenig-editor/addon/components/koenig-card-embed/nft.hbs
|
add|ember-template-lint|require-valid-alt-text|3|44|3|44|a7f0566c430150bae4153e0dfb489a218bdeb8a4|1658102400000|1668474000000|1673658000000|lib/koenig-editor/addon/components/koenig-card-embed/nft.hbs
|
||||||
add|ember-template-lint|require-valid-alt-text|8|20|8|20|9d0c591086dc9139ff38a7b385c3367a83438786|1658102400000|1668474000000|1673658000000|lib/koenig-editor/addon/components/koenig-card-embed/nft.hbs
|
add|ember-template-lint|require-valid-alt-text|8|20|8|20|9d0c591086dc9139ff38a7b385c3367a83438786|1658102400000|1668474000000|1673658000000|lib/koenig-editor/addon/components/koenig-card-embed/nft.hbs
|
||||||
add|ember-template-lint|require-input-label|10|12|10|12|8c3c0ea315ff4da828363989a45fa11256a78796|1658102400000|1668474000000|1673658000000|lib/koenig-editor/addon/components/koenig-card-image/selector-tenor.hbs
|
add|ember-template-lint|require-input-label|10|12|10|12|8c3c0ea315ff4da828363989a45fa11256a78796|1658102400000|1668474000000|1673658000000|lib/koenig-editor/addon/components/koenig-card-image/selector-tenor.hbs
|
||||||
|
remove|ember-template-lint|no-down-event-binding|5|13|5|13|a158be60fde14211f6abfbac329c94fa5a3b4a46|1658102400000|1668474000000|1673658000000|app/components/gh-koenig-editor.hbs
|
||||||
|
remove|ember-template-lint|no-invalid-interactive|5|8|5|8|94046126dd697b080aa16222b00a3d5a545ee001|1658102400000|1668474000000|1673658000000|app/components/gh-koenig-editor.hbs
|
||||||
|
remove|ember-template-lint|no-invalid-interactive|6|8|6|8|94046126dd697b080aa16222b00a3d5a545ee001|1658102400000|1668474000000|1673658000000|app/components/gh-koenig-editor.hbs
|
||||||
|
remove|ember-template-lint|no-passed-in-event-handlers|26|12|26|12|4ad1176b9a4bb23d79114735b478240166113502|1658102400000|1668474000000|1673658000000|app/components/gh-koenig-editor.hbs
|
||||||
|
remove|ember-template-lint|no-passed-in-event-handlers|28|12|28|12|7ffe0a55c81efcfcd46880a930fa69bee73835b3|1658102400000|1668474000000|1673658000000|app/components/gh-koenig-editor.hbs
|
||||||
|
59
ghost/admin/app/components/gh-koenig-editor-react.hbs
Normal file
59
ghost/admin/app/components/gh-koenig-editor-react.hbs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<div class="gh-koenig-editor relative w-100 vh-100 overflow-x-hidden overflow-y-auto z-0" {{did-insert this.registerElement}} ...attributes>
|
||||||
|
{{!-- full height content pane --}}
|
||||||
|
{{!-- template-lint-disable no-down-event-binding no-invalid-interactive no-passed-in-event-handlers --}}
|
||||||
|
<div
|
||||||
|
class="gh-koenig-editor-pane flex flex-column mih-100"
|
||||||
|
{{on "mousedown" this.trackMousedown}}
|
||||||
|
{{on "mouseup" this.focusEditor}}
|
||||||
|
>
|
||||||
|
<GhEditorFeatureImage
|
||||||
|
@image={{@featureImage}}
|
||||||
|
@updateImage={{@setFeatureImage}}
|
||||||
|
@clearImage={{@clearFeatureImage}}
|
||||||
|
@alt={{@featureImageAlt}}
|
||||||
|
@updateAlt={{@setFeatureImageAlt}}
|
||||||
|
@caption={{@featureImageCaption}}
|
||||||
|
@updateCaption={{@setFeatureImageCaption}}
|
||||||
|
@forceButtonDisplay={{or (not @title) (eq @title "(Untitled)") this.titleIsHovered this.titleIsFocused}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<GhTextarea
|
||||||
|
@class="gh-editor-title"
|
||||||
|
@placeholder={{@titlePlaceholder}}
|
||||||
|
@shouldFocus={{or @titleAutofocus false}}
|
||||||
|
@tabindex="1"
|
||||||
|
@autoExpand=".gh-koenig-editor"
|
||||||
|
@value={{readonly this.title}}
|
||||||
|
@input={{this.updateTitle}}
|
||||||
|
@focus-out={{optional @onTitleBlur}}
|
||||||
|
@keyDown={{this.onTitleKeydown}}
|
||||||
|
@didCreateTextarea={{this.registerTitleElement}}
|
||||||
|
{{on "focus" (fn (mut this.titleIsFocused) true)}}
|
||||||
|
{{on "blur" (fn (mut this.titleIsFocused) false)}}
|
||||||
|
{{on "mouseover" (fn (mut this.titleIsHovered) true)}}
|
||||||
|
{{on "mouseleave" (fn (mut this.titleIsHovered) false)}}
|
||||||
|
data-test-editor-title-input={{true}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<KoenigEditor
|
||||||
|
@mobiledoc={{@body}}
|
||||||
|
@placeholder={{@bodyPlaceholder}}
|
||||||
|
@spellcheck={{true}}
|
||||||
|
@onChange={{@onBodyChange}}
|
||||||
|
@didCreateEditor={{this.onEditorCreated}}
|
||||||
|
@cursorDidExitAtTop={{this.focusTitle}}
|
||||||
|
@headerOffset={{@headerOffset}}
|
||||||
|
@dropTargetSelector=".gh-koenig-editor-pane"
|
||||||
|
@scrollContainerSelector={{@scrollContainerSelector}}
|
||||||
|
@scrollOffsetTopSelector={{@scrollOffsetTopSelector}}
|
||||||
|
@scrollOffsetBottomSelector={{@scrollOffsetBottomSelector}}
|
||||||
|
@wordCountDidChange={{@onWordCountChange}}
|
||||||
|
@snippets={{@snippets}}
|
||||||
|
@saveSnippet={{@saveSnippet}}
|
||||||
|
@updateSnippet={{@updateSnippet}}
|
||||||
|
@deleteSnippet={{@deleteSnippet}}
|
||||||
|
@cardOptions={{@cardOptions}}
|
||||||
|
@postType={{@postType}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
154
ghost/admin/app/components/gh-koenig-editor-react.js
Normal file
154
ghost/admin/app/components/gh-koenig-editor-react.js
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
import Component from '@glimmer/component';
|
||||||
|
import {action} from '@ember/object';
|
||||||
|
import {tracked} from '@glimmer/tracking';
|
||||||
|
|
||||||
|
export default class GhKoenigEditorReactComponent extends Component {
|
||||||
|
containerElement = null;
|
||||||
|
titleElement = null;
|
||||||
|
koenigEditor = null;
|
||||||
|
mousedownY = 0;
|
||||||
|
|
||||||
|
@tracked titleIsHovered = false;
|
||||||
|
@tracked titleIsFocused = false;
|
||||||
|
|
||||||
|
get title() {
|
||||||
|
return this.args.title === '(Untitled)' ? '' : this.args.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
registerElement(element) {
|
||||||
|
this.containerElement = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
trackMousedown(event) {
|
||||||
|
// triggered when a mousedown is registered on .gh-koenig-editor-pane
|
||||||
|
this.mousedownY = event.clientY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Title actions -----------------------------------------------------------
|
||||||
|
|
||||||
|
@action
|
||||||
|
registerTitleElement(element) {
|
||||||
|
this.titleElement = element;
|
||||||
|
|
||||||
|
// this is needed because focus event handler won't be fired if input has focus when rendering
|
||||||
|
if (this.titleElement === document.activeElement) {
|
||||||
|
this.titleIsFocused = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
updateTitle(event) {
|
||||||
|
this.args.onTitleChange?.(event.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
focusTitle() {
|
||||||
|
this.titleElement.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
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.koenigEditor.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Body actions ------------------------------------------------------------
|
||||||
|
|
||||||
|
@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.koenigEditor = koenig.editor;
|
||||||
|
|
||||||
|
// focus the title when pressing SHIFT+TAB
|
||||||
|
this.koenigEditor.registerKeyCommand({
|
||||||
|
str: 'SHIFT+TAB',
|
||||||
|
run() {
|
||||||
|
component.focusTitle();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_addParaAtTop() {
|
||||||
|
if (!this.koenigEditor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
// a blank paragraph
|
||||||
|
if (section.isListItem || !section.isBlank || section.text !== '') {
|
||||||
|
editor.run((postEditor) => {
|
||||||
|
let {builder} = postEditor;
|
||||||
|
let newPara = builder.createMarkupSection('p');
|
||||||
|
let sections = section.isListItem ? section.parent.parent.sections : section.parent.sections;
|
||||||
|
|
||||||
|
postEditor.insertSectionBefore(sections, newPara, section);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
<div class="gh-koenig-editor relative w-100 vh-100 overflow-x-hidden overflow-y-auto z-0" {{did-insert this.registerElement}} ...attributes>
|
<div class="gh-koenig-editor relative w-100 vh-100 overflow-x-hidden overflow-y-auto z-0" {{did-insert this.registerElement}} ...attributes>
|
||||||
{{!-- full height content pane --}}
|
{{!-- full height content pane --}}
|
||||||
|
{{!-- template-lint-disable no-down-event-binding no-invalid-interactive no-passed-in-event-handlers --}}
|
||||||
<div
|
<div
|
||||||
class="gh-koenig-editor-pane flex flex-column mih-100"
|
class="gh-koenig-editor-pane flex flex-column mih-100"
|
||||||
{{on "mousedown" this.trackMousedown}}
|
{{on "mousedown" this.trackMousedown}}
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
gh-koenig-editor acts as a wrapper around the title input and
|
gh-koenig-editor acts as a wrapper around the title input and
|
||||||
koenig editor canvas to support Ghost-specific editor behaviour
|
koenig editor canvas to support Ghost-specific editor behaviour
|
||||||
--}}
|
--}}
|
||||||
<GhKoenigEditor
|
<GhKoenigEditorReact
|
||||||
@title={{readonly this.post.titleScratch}}
|
@title={{readonly this.post.titleScratch}}
|
||||||
@titleAutofocus={{this.shouldFocusTitle}}
|
@titleAutofocus={{this.shouldFocusTitle}}
|
||||||
@titlePlaceholder={{concat (capitalize this.post.displayName) " title"}}
|
@titlePlaceholder={{concat (capitalize this.post.displayName) " title"}}
|
||||||
|
Loading…
Reference in New Issue
Block a user