mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-04 08:54:36 +03:00
aaed8d9cf4
* replace `isEditing` observers with `didReceiveAttrs` hook * 🐛 Fix HTML card not launching in edit mode closes https://github.com/TryGhost/Ghost/issues/8310 - adds `autofocus=true` attribute to `gh-cm-editor` that will use CodeMirror's built-in autofocus behaviour - set HTML card's launch mode to `edit` and ensure that the `autofocus` attribute is passed - refactor `gh-cm-editor` for more robust event handling - re-work `ch-cm-editor` tests to take into account CMs events not being triggered within a single run-loop and to still work when the browser window isn't focused (should fix the random test failures on Travis and the issues where the CM tests will fail locally)
109 lines
3.2 KiB
JavaScript
109 lines
3.2 KiB
JavaScript
/* global CodeMirror */
|
|
import Component from 'ember-component';
|
|
import {bind, once, scheduleOnce} from 'ember-runloop';
|
|
import injectService from 'ember-service/inject';
|
|
import RSVP from 'rsvp';
|
|
import {assign} from 'ember-platform';
|
|
import boundOneWay from 'ghost-admin/utils/bound-one-way';
|
|
import {InvokeActionMixin} from 'ember-invoke-action';
|
|
|
|
const CmEditorComponent = Component.extend(InvokeActionMixin, {
|
|
classNameBindings: ['isFocused:focused'],
|
|
|
|
_value: boundOneWay('value'), // make sure a value exists
|
|
isFocused: false,
|
|
|
|
// options for the editor
|
|
lineNumbers: true,
|
|
indentUnit: 4,
|
|
mode: 'htmlmixed',
|
|
theme: 'xq-light',
|
|
|
|
_editor: null, // reference to CodeMirror editor
|
|
|
|
lazyLoader: injectService(),
|
|
|
|
didInsertElement() {
|
|
this._super(...arguments);
|
|
|
|
let loader = this.get('lazyLoader');
|
|
|
|
RSVP.all([
|
|
loader.loadStyle('codemirror', 'assets/codemirror/codemirror.css'),
|
|
loader.loadScript('codemirror', 'assets/codemirror/codemirror.js')
|
|
]).then(() => {
|
|
scheduleOnce('afterRender', this, function () {
|
|
this._initCodeMirror();
|
|
});
|
|
});
|
|
},
|
|
|
|
_initCodeMirror() {
|
|
let options = this.getProperties('lineNumbers', 'indentUnit', 'mode', 'theme', 'autofocus');
|
|
assign(options, {value: this.get('_value')});
|
|
|
|
this._editor = new CodeMirror(this.element, options);
|
|
|
|
// by default CodeMirror will place the cursor at the beginning of the
|
|
// content, it makes more sense for the cursor to be at the end
|
|
if (options.autofocus) {
|
|
this._editor.setCursor(this._editor.lineCount(), 0);
|
|
}
|
|
|
|
// events
|
|
this._setupCodeMirrorEventHandler('focus', this, this._focus);
|
|
this._setupCodeMirrorEventHandler('blur', this, this._blur);
|
|
this._setupCodeMirrorEventHandler('change', this, this._update);
|
|
},
|
|
|
|
_setupCodeMirrorEventHandler(event, target, method) {
|
|
let callback = bind(target, method);
|
|
|
|
this._editor.on(event, callback);
|
|
|
|
this.one('willDestroyElement', this, function () {
|
|
this._editor.off(event, callback);
|
|
});
|
|
},
|
|
|
|
_update(codeMirror, changeObj) {
|
|
once(this, this._invokeUpdateAction, codeMirror.getValue(), codeMirror, changeObj);
|
|
},
|
|
|
|
_invokeUpdateAction(...args) {
|
|
this.invokeAction('update', ...args);
|
|
},
|
|
|
|
_focus(codeMirror, event) {
|
|
this.set('isFocused', true);
|
|
once(this, this._invokeFocusAction, codeMirror.getValue(), codeMirror, event);
|
|
},
|
|
|
|
_invokeFocusAction(...args) {
|
|
this.invokeAction('focus-in', ...args);
|
|
},
|
|
|
|
_blur(/* codeMirror, event */) {
|
|
this.set('isFocused', false);
|
|
},
|
|
|
|
willDestroyElement() {
|
|
this._super(...arguments);
|
|
|
|
// Ensure the editor exists before trying to destroy it. This fixes
|
|
// an error that occurs if codemirror hasn't finished loading before
|
|
// the component is destroyed.
|
|
if (this._editor) {
|
|
let editor = this._editor.getWrapperElement();
|
|
editor.parentNode.removeChild(editor);
|
|
this._editor = null;
|
|
}
|
|
}
|
|
});
|
|
|
|
CmEditorComponent.reopenClass({
|
|
positionalParams: ['value']
|
|
});
|
|
|
|
export default CmEditorComponent;
|