Ghost/ghost/admin/app/components/gh-cm-editor.js
Kevin Ansfield b425cb1352 🎨 fully fix Code Injection styles (#723)
closes https://github.com/TryGhost/Ghost/issues/8504

- allow `.settings-code-editor` to inherit from `.gh-input` - fixes border colours
- change `gh-cm-editor`'s "focused" class from `.focused` to `.focus` so it can inherit `.gh-input` styles
- add lazy loading of CM styles back in `gh-cm-editor` but prevent `lib/codemirror.css` from being compiled into the codemirror styles so that we get the theme colours without causing conflicts with the main editor
2017-06-01 23:01:43 +02:00

109 lines
3.2 KiB
JavaScript

/* global CodeMirror */
import Component from 'ember-component';
import RSVP from 'rsvp';
import boundOneWay from 'ghost-admin/utils/bound-one-way';
import injectService from 'ember-service/inject';
import {InvokeActionMixin} from 'ember-invoke-action';
import {assign} from 'ember-platform';
import {bind, once, scheduleOnce} from 'ember-runloop';
const CmEditorComponent = Component.extend(InvokeActionMixin, {
classNameBindings: ['isFocused:focus'],
_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;