feat(Code Editor): Ace Editor for Code fieldtypes

- Better editing experience with syntax highlighting and auto-completion
- Support HTML, Javascript and CSS language modes via the Options field
This commit is contained in:
Faris Ansari 2018-11-06 00:59:14 +05:30
parent 6beac087db
commit c88f05d711
4 changed files with 85 additions and 5 deletions

View file

@ -1,8 +1,78 @@
frappe.ui.form.ControlCode = frappe.ui.form.ControlText.extend({
make_input: function() {
this._super();
$(this.input_area).find("textarea")
.allowTabs()
.addClass('control-code');
make_input() {
if (this.editor) return;
this.load_lib().then(() => this.make_ace_editor());
},
make_ace_editor() {
const ace_editor_target = $('<div class="ace-editor-target"></div>')
.appendTo(this.input_area);
// styling
ace_editor_target.addClass('border rounded');
ace_editor_target.css('height', 300);
// initialize
this.editor = ace.edit(ace_editor_target.get(0));
this.editor.setTheme('ace/theme/tomorrow');
this.set_language();
// events
this.editor.session.on('change', frappe.utils.debounce((delta) => {
const input_value = this.get_input_value();
this.parse_validate_and_set_in_model(input_value);
}, 300));
},
set_language() {
const language_map = {
'Javascript': 'ace/mode/javascript',
'HTML': 'ace/mode/html',
'CSS': 'ace/mode/css'
}
const language = this.df.options;
const valid_languages = Object.keys(language_map);
if (!valid_languages.includes(language)) {
console.warn(`Invalid language option provided for field "${this.df.label}". Valid options are ${valid_languages.join(', ')}.`);
}
const ace_language_mode = language_map[language] || '';
this.editor.session.setMode(ace_language_mode);
},
parse(value) {
if (value == null) {
value = "";
}
return value;
},
set_formatted_input(value) {
this.library_loaded.then(() => {
if (value === this.get_input_value()) return;
this.editor.session.setValue(value);
});
},
get_input_value() {
return this.editor ? this.editor.session.getValue() : '';
},
load_lib() {
if (frappe.boot.developer_mode) {
this.root_lib_path = '/assets/frappe/js/lib/ace-builds/src-noconflict/';
} else {
this.root_lib_path = '/assets/frappe/js/lib/ace-builds/src-min-noconflict/';
}
this.library_loaded = new Promise(resolve => {
frappe.require(this.root_lib_path + 'ace.js', () => {
window.ace.config.set('basePath', this.root_lib_path);
resolve();
});
});
return this.library_loaded;
}
});

View file

@ -174,6 +174,10 @@ a.badge-hover& {
border: 1px solid @border-color;
}
.rounded {
border-radius: 4px;
}
.close-inline {
font-size: 120%;
font-weight: bold;

View file

@ -16,6 +16,7 @@
},
"homepage": "https://frappe.io",
"dependencies": {
"ace-builds": "^1.4.1",
"awesomplete": "^1.1.2",
"cookie": "^0.3.1",
"express": "^4.16.2",

View file

@ -58,6 +58,11 @@ accepts@~1.3.4:
mime-types "~2.1.16"
negotiator "0.6.1"
ace-builds@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/ace-builds/-/ace-builds-1.4.1.tgz#a66e33bcd519918ee99bc54cac5d7399a83c8ff2"
integrity sha512-ulsfTOi/oAXBIt9hYlK4asyFZYsFOOUAbwBZidbjWWJycRX4LtwIithMyndcnoQ4PavkZ0iGJ+c9nqHNiYbubA==
acorn-dynamic-import@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz#901ceee4c7faaef7e07ad2a47e890675da50a278"