code editor ux improvements

* Save and restore editor selection(s), cursor(s), scroll pos etc
* Improve focusing of editor at appropriate times
* Works with both ace and monaco
* Backwards compatible and (almost) fully functional with existing nodes
This commit is contained in:
Steve-Mcl 2022-04-27 11:23:13 +01:00
parent d802ce1484
commit 194eb4e266
16 changed files with 322 additions and 95 deletions

View File

@ -198,6 +198,8 @@
}
RED.editor.editJSON({
value: value,
stateId: RED.editor.generateViewStateId("typedInput", that, "json"),
focus: true,
complete: function(v) {
var value = v;
try {
@ -220,6 +222,8 @@
var that = this;
RED.editor.editExpression({
value: this.value().replace(/\t/g,"\n"),
stateId: RED.editor.generateViewStateId("typedInput", that, "jsonata"),
focus: true,
complete: function(v) {
that.value(v.replace(/\n/g,"\t"));
}
@ -234,6 +238,8 @@
var that = this;
RED.editor.editBuffer({
value: this.value(),
stateId: RED.editor.generateViewStateId("typedInput", that, "bin"),
focus: true,
complete: function(v) {
that.value(v);
}

View File

@ -796,6 +796,7 @@ RED.editor = (function() {
if (buildingEditDialog) { return }
buildingEditDialog = true;
var editing_node = node;
var removeInfoEditorOnClose = false;
var skipInfoRefreshOnClose = false;
var activeEditPanes = [];
@ -991,6 +992,14 @@ RED.editor = (function() {
}
if (!node._def.defaults || !node._def.defaults.hasOwnProperty('info')) {
nodeEditPanes.push('editor-tab-description');
removeInfoEditorOnClose = true;
if(node.infoEditor) {
//As 'editor-tab-description' adds `node.infoEditor` store original & set a
//flag to NOT remove this property
node.infoEditor__orig = node.infoEditor;
delete node.infoEditor;
removeInfoEditorOnClose = false;
}
}
nodeEditPanes.push("editor-tab-appearance");
@ -1006,8 +1015,17 @@ RED.editor = (function() {
if (RED.view.state() != RED.state.IMPORT_DRAGGING) {
RED.view.state(RED.state.DEFAULT);
}
if (editing_node && !skipInfoRefreshOnClose) {
RED.sidebar.info.refresh(editing_node);
if (editing_node) {
if (editing_node.infoEditor__orig) {
editing_node.infoEditor = editing_node.infoEditor__orig;
delete editing_node.infoEditor__orig;
}
if (removeInfoEditorOnClose) {
delete editing_node.infoEditor;
}
if (!skipInfoRefreshOnClose) {
RED.sidebar.info.refresh(editing_node);
}
}
RED.workspaces.refresh();
@ -1867,6 +1885,48 @@ RED.editor = (function() {
}
}
/** Genrate a consistent but unique ID for saving and restoring the code editors view state */
function generateViewStateId(source, thing, suffix) {
try {
thing = thing || {};
const thingOptions = typeof thing.options === "object" ? thing.options : {};
let stateId;
if (thing.hasOwnProperty("stateId")) {
stateId = thing.stateId
} else if (thingOptions.hasOwnProperty("stateId")) {
stateId = thing.stateId
}
if (stateId === false) { return false; }
if (!stateId) {
let id;
const selection = RED.view.selection();
if (source === "node" && thing.id) {
id = thing.id;
} else if (selection.nodes && selection.nodes.length) {
id = selection.nodes[0].id;
} else {
return false; //cant obtain Id.
}
//Use a string builder to build an ID
const sb = [id];
//get the index of the el - there may be more than one editor.
const el = $(thing.element || thingOptions.element);
if(el.length) {
sb.push(el.closest(".form-row").index());
sb.push(el.index());
}
if (source == "typedInput") {
sb.push(el.closest("li").index());//for when embeded in editable list
if (!suffix && thing.propertyType) { suffix = thing.propertyType }
}
stateId = sb.join("/");
}
if (stateId && suffix) { stateId += "/" + suffix; };
return stateId;
} catch (error) {
return false;
}
}
return {
init: function() {
if(window.ace) { window.ace.config.set('basePath', 'vendor/ace'); }
@ -1883,6 +1943,7 @@ RED.editor = (function() {
});
RED.editor.codeEditor.init();
},
generateViewStateId: generateViewStateId,
edit: showEditDialog,
editConfig: showEditConfigNodeDialog,
editFlow: showEditFlowDialog,

View File

@ -47,6 +47,7 @@
var definition = {
show: function(options) {
var value = options.value;
var onCancel = options.cancel;
var onComplete = options.complete;
var type = "_buffer"
if ($("script[data-template-name='"+type+"']").length === 0) {
@ -60,12 +61,14 @@
var trayOptions = {
title: options.title,
focusElement: options.focusElement,
width: "inherit",
buttons: [
{
id: "node-dialog-cancel",
text: RED._("common.label.cancel"),
click: function() {
if (onCancel) { onCancel() };
RED.tray.close();
}
},
@ -74,7 +77,8 @@
text: RED._("common.label.done"),
class: "primary",
click: function() {
onComplete(JSON.stringify(bufferBinValue));
bufferStringEditor.saveView();
if (onComplete) { onComplete(JSON.stringify(bufferBinValue),null,bufferStringEditor); }
RED.tray.close();
}
}
@ -86,19 +90,20 @@
}
},
open: function(tray) {
var trayBody = tray.find('.red-ui-tray-body');
var dialogForm = RED.editor.buildEditForm(tray.find('.red-ui-tray-body'),'dialog-form',type,'editor');
bufferStringEditor = RED.editor.createEditor({
id: 'red-ui-editor-type-buffer-str',
value: "",
value: value||"",
stateId: RED.editor.generateViewStateId("buffer", options, ""),
focus: true,
mode:"ace/mode/text"
});
bufferStringEditor.getSession().setValue(value||"",-1);
bufferBinEditor = RED.editor.createEditor({
id: 'red-ui-editor-type-buffer-bin',
value: "",
stateId: false,
focus: false,
mode:"ace/mode/text",
readOnly: true
});

View File

@ -80,6 +80,9 @@ RED.editor.codeEditor.ace = (function() {
}
},100);
}
if (!options.stateId && options.stateId !== false) {
options.stateId = RED.editor.generateViewStateId("ace", options, (options.mode || options.title).split("/").pop());
}
if (options.mode === 'ace/mode/markdown') {
$(el).addClass("red-ui-editor-text-container-toolbar");
editor.toolbar = RED.editor.customEditTypes['_markdown'].buildToolbar(toolbarRow,editor);
@ -92,11 +95,15 @@ RED.editor.codeEditor.ace = (function() {
RED.editor.editMarkdown({
value: value,
width: "Infinity",
cursor: editor.getCursorPosition(),
stateId: options.stateId,
focus: true,
cancel: function () {
editor.focus();
},
complete: function(v,cursor) {
editor.setValue(v, -1);
editor.gotoLine(cursor.row+1,cursor.column,false);
setTimeout(function() {
editor.restoreView();
editor.focus();
},300);
}
@ -117,11 +124,56 @@ RED.editor.codeEditor.ace = (function() {
editor._destroy = editor.destroy;
editor.destroy = function() {
try {
editor.saveView();
editor._initState = null;
this._destroy();
} catch (e) { }
$(el).remove();
$(toolbarRow).remove();
}
editor.on("blur", function () {
editor.focusMemory = false;
editor.saveView();
})
editor.on("focus", function () {
if (editor._initState) {
editor.restoreView(editor._initState);
editor._initState = null;
}
})
editor.getView = function () {
var session = editor.getSession();
return {
selection: session.selection.toJSON(),
scrollTop: session.getScrollTop(),
scrollLeft: session.getScrollLeft(),
options: session.getOptions()
}
}
editor.saveView = function () {
if (!options.stateId) { return; } //only possible if created with a unique stateId
window._editorStateAce = window._editorStateAce || {};
var state = editor.getView();
window._editorStateAce[options.stateId] = state;
return state;
}
editor.restoreView = function (state) {
if (!options.stateId) { return; } //only possible if created with a unique stateId
window._editorStateAce = window._editorStateAce || {};
var _state = state || window._editorStateAce[options.stateId];
if (!_state) { return; } //no view state available
try {
var session = editor.getSession();
session.setOptions(_state.options);
session.selection.fromJSON(_state.selection);
session.setScrollTop(_state.scrollTop);
session.setScrollLeft(_state.scrollLeft);
editor._initState = _state;
} catch (error) {
delete window._editorStateMonaco[options.stateId];
}
};
editor.restoreView();
editor.type = type;
return editor;
}

View File

@ -171,7 +171,7 @@ RED.editor.codeEditor.monaco = (function() {
options = options || {};
window.MonacoEnvironment = window.MonacoEnvironment || {};
window.MonacoEnvironment.getWorkerUrl = function (moduleId, label) {
window.MonacoEnvironment.getWorkerUrl = window.MonacoEnvironment.getWorkerUrl || function (moduleId, label) {
if (label === 'json') { return './vendor/monaco/dist/json.worker.js'; }
if (label === 'css' || label === 'scss') { return './vendor/monaco/dist/css.worker.js'; }
if (label === 'html' || label === 'handlebars') { return './vendor/monaco/dist/html.worker.js'; }
@ -747,13 +747,25 @@ RED.editor.codeEditor.monaco = (function() {
mode = "html";
break;
case "appcache":
case "sh":
case "bash":
mode = "shell";
break;
case "batchfile":
mode = "bat";
break;
case "protobuf":
mode = "proto";
break;
//TODO: add other compatability types.
}
return mode;
}
if(!options.stateId && options.stateId !== false) {
options.stateId = RED.editor.generateViewStateId("monaco", options, (options.mode || options.title).split("/").pop());
}
var el = options.element || $("#"+options.id)[0];
var toolbarRow = $("<div>").appendTo(el);
el = $("<div>").appendTo(el).addClass("red-ui-editor-text-container")[0];
@ -1098,6 +1110,7 @@ RED.editor.codeEditor.monaco = (function() {
try {
var m = this.getModel();
if(m && !m.isDisposed()) {
ed._initState = null;
m.dispose();
}
this.setModel(null);
@ -1243,14 +1256,7 @@ RED.editor.codeEditor.monaco = (function() {
//#endregion "ACE compatability"
//final setup
if (options.cursor) {
var row = options.cursor.row || options.cursor.lineNumber;
var col = options.cursor.column || options.cursor.col;
ed.gotoLine(row, col);
}
if (options.focus) {
ed.focus();
}
ed.focusMemory = options.focus;
ed._mode = editorOptions.language;
//as models are signleton, consts and let are avialable to other javascript instances
@ -1262,11 +1268,12 @@ RED.editor.codeEditor.monaco = (function() {
}
ed.onDidBlurEditorWidget(function() {
ed.focusMemory = false;
ed.saveView();
if(isVisible(el) == false) {
onVisibilityChange(false, 0, el);
}
});
ed.onDidFocusEditorWidget(function() {
onVisibilityChange(true, 10, el);
});
@ -1300,17 +1307,33 @@ RED.editor.codeEditor.monaco = (function() {
}
function onVisibilityChange(visible, delay, element) {
if(visible) {
if(ed._mode == "javascript" && ed._tempMode == "text") {
delay = delay || 50;
if (visible) {
if (ed.focusMemory) {
setTimeout(function () {
if (element.parentElement) { //ensure el is still in DOM
ed.focus();
}
}, 300)
}
if (ed._initState) {
setTimeout(function () {
if (element.parentElement) { //ensure el is still in DOM
ed.restoreViewState(ed._initState);
ed._initState = null;
}
}, delay);
}
if (ed._mode == "javascript" && ed._tempMode == "text") {
ed._tempMode = "";
setTimeout(function() {
if(element.parentElement) { //ensure el is still in DOM
setTimeout(function () {
if (element.parentElement) { //ensure el is still in DOM
ed.setMode('javascript', undefined, false);
}
}, delay || 50);
}, delay);
}
} else if(ed._mode == "javascript" && ed._tempMode != "text") {
if(element.parentElement) { //ensure el is still in DOM
} else if (ed._mode == "javascript" && ed._tempMode != "text") {
if (element.parentElement) { //ensure el is still in DOM
ed.setMode('text', undefined, false);
ed._tempMode = "text";
}
@ -1329,15 +1352,19 @@ RED.editor.codeEditor.monaco = (function() {
expandButton.on("click", function (e) {
e.preventDefault();
var value = ed.getValue();
ed.saveView();
RED.editor.editMarkdown({
value: value,
width: "Infinity",
cursor: ed.getCursorPosition(),
stateId: options.stateId,
cancel: function () {
ed.focus();
},
complete: function (v, cursor) {
ed.setValue(v, -1);
ed.gotoLine(cursor.row + 1, cursor.column, false);
setTimeout(function () {
ed.focus();
ed.restoreView();
}, 300);
}
})
@ -1353,7 +1380,37 @@ RED.editor.codeEditor.monaco = (function() {
autoClose: 50
});
}
ed.getView = function () {
return ed.saveViewState();
}
ed.saveView = function (debuginfo) {
if (!options.stateId) { return; } //only possible if created with a unique stateId
window._editorStateMonaco = window._editorStateMonaco || {};
var state = ed.getView();
window._editorStateMonaco[options.stateId] = state;
return state;
}
ed.restoreView = function (state) {
if (!options.stateId) { return; } //only possible if created with a unique stateId
window._editorStateMonaco = window._editorStateMonaco || {};
var _state = state || window._editorStateMonaco[options.stateId];
if (!_state) { return; } //no view state available
try {
if (ed.type) { //is editor already initialised?
ed.restoreViewState(_state);
} else {
ed._initState = _state;
}
} catch (error) {
delete window._editorStateMonaco[options.stateId];
}
};
ed.restoreView();
if (options.cursor && !ed._initState) {
var row = options.cursor.row || options.cursor.lineNumber;
var col = options.cursor.column || options.cursor.col;
ed.gotoLine(row, col);
}
ed.type = type;
return ed;
}

View File

@ -50,6 +50,7 @@
show: function(options) {
var expressionTestCacheId = options.parent||"_";
var value = options.value;
var onCancel = options.cancel;
var onComplete = options.complete;
var type = "_expression"
if ($("script[data-template-name='"+type+"']").length === 0) {
@ -63,12 +64,14 @@
var trayOptions = {
title: options.title,
focusElement: options.focusElement,
width: "inherit",
buttons: [
{
id: "node-dialog-cancel",
text: RED._("common.label.cancel"),
click: function() {
if(onCancel) { onCancel() };
RED.tray.close();
}
},
@ -78,7 +81,8 @@
class: "primary",
click: function() {
$("#red-ui-editor-type-expression-help").text("");
onComplete(expressionEditor.getValue());
expressionEditor.saveView();
if (onComplete) { onComplete(expressionEditor.getValue(),expressionEditor.getCursorPosition(),expressionEditor); }
RED.tray.close();
}
}
@ -110,6 +114,8 @@
id: 'red-ui-editor-type-expression',
value: "",
mode:"ace/mode/jsonata",
stateId: options.stateId,
focus: true,
options: {
enableBasicAutocompletion:true,
enableSnippets:true,
@ -233,6 +239,8 @@
testDataEditor = RED.editor.createEditor({
id: 'red-ui-editor-type-expression-test-data',
value: expressionTestCache[expressionTestCacheId] || '{\n "payload": "hello world"\n}',
stateId: false,
focus: false,
mode:"ace/mode/json",
lineNumbers: false
});
@ -302,6 +310,8 @@
testResultEditor = RED.editor.createEditor({
id: 'red-ui-editor-type-expression-test-result',
value: "",
stateId: false,
focus: false,
mode:"ace/mode/json",
lineNumbers: false,
readOnly: true
@ -346,6 +356,9 @@
expressionEditor.destroy();
testDataEditor.destroy();
testResultEditor.destroy();
delete expressionEditor;
delete testDataEditor;
delete testResultEditor;
},
show: function() {}
}

View File

@ -21,6 +21,7 @@
var definition = {
show: function(options) {
var value = options.value;
var onCancel = options.cancel;
var onComplete = options.complete;
var type = "_js"
if ($("script[data-template-name='"+type+"']").length === 0) {
@ -28,16 +29,16 @@
}
RED.view.state(RED.state.EDITING);
var expressionEditor;
var changeTimer;
var trayOptions = {
title: options.title,
focusElement: options.focusElement,
width: options.width||"inherit",
buttons: [
{
id: "node-dialog-cancel",
text: RED._("common.label.cancel"),
click: function() {
if (onCancel) { onCancel() };
RED.tray.close();
}
},
@ -46,7 +47,8 @@
text: RED._("common.label.done"),
class: "primary",
click: function() {
onComplete(expressionEditor.getValue(),expressionEditor.getCursorPosition());
expressionEditor.saveView();
if (onComplete) { onComplete(expressionEditor.getValue(), expressionEditor.getCursorPosition(), expressionEditor); }
RED.tray.close();
}
}
@ -62,11 +64,12 @@
expressionEditor.resize();
},
open: function(tray) {
var trayBody = tray.find('.red-ui-tray-body');
var dialogForm = RED.editor.buildEditForm(tray.find('.red-ui-tray-body'),'dialog-form',type,'editor');
expressionEditor = RED.editor.createEditor({
id: 'node-input-js',
mode: options.mode || 'ace/mode/javascript',
stateId: options.stateId,
focus: true,
value: value,
globals: {
msg:true,
@ -84,19 +87,17 @@
},
extraLibs: options.extraLibs
});
if (options.cursor) {
if (options.cursor && !expressionEditor._initState) {
expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false);
}
dialogForm.i18n();
setTimeout(function() {
expressionEditor.focus();
},300);
},
close: function() {
expressionEditor.destroy();
if (options.onclose) {
options.onclose();
}
expressionEditor.destroy();
delete expressionEditor;
},
show: function() {}
}

View File

@ -434,6 +434,7 @@
var definition = {
show: function(options) {
var value = options.value;
var onCancel = options.cancel;
var onComplete = options.complete;
var type = "_json"
if ($("script[data-template-name='"+type+"']").length === 0) {
@ -455,15 +456,16 @@
}
}
var rootNode;
var trayOptions = {
title: options.title,
focusElement: options.focusElement,
width: options.width||700,
buttons: [
{
id: "node-dialog-cancel",
text: RED._("common.label.cancel"),
click: function() {
if (onCancel) { onCancel() };
RED.tray.close();
}
},
@ -485,7 +487,8 @@
} else if (activeTab === "json-raw") {
result = expressionEditor.getValue();
}
if (onComplete) { onComplete(result) }
expressionEditor.saveView();
if (onComplete) { onComplete(result,null,expressionEditor) };
RED.tray.close();
}
}
@ -531,10 +534,12 @@
expressionEditor = RED.editor.createEditor({
id: 'node-input-json',
value: "",
mode:"ace/mode/json"
value: value||"",
mode:"ace/mode/json",
stateId: options.stateId,
focus: true
});
expressionEditor.getSession().setValue(value||"",-1);
if (options.requireValid) {
expressionEditor.getSession().on('change', function() {
clearTimeout(changeTimer);
@ -598,14 +603,13 @@
content: $("#red-ui-editor-type-json-tab-ui")
});
finishedBuild = true;
},
close: function() {
if (options.onclose) {
options.onclose();
}
expressionEditor.destroy();
delete expressionEditor;
},
show: function() {}
}

View File

@ -54,24 +54,26 @@
var definition = {
show: function(options) {
var value = options.value;
var onCancel = options.cancel;
var onComplete = options.complete;
var type = "_markdown"
if ($("script[data-template-name='"+type+"']").length === 0) {
$(template).appendTo("#red-ui-editor-node-configs");
}
RED.view.state(RED.state.EDITING);
var expressionEditor;
var trayOptions = {
title: options.title,
focusElement: options.focusElement,
width: options.width||Infinity,
buttons: [
{
id: "node-dialog-cancel",
text: RED._("common.label.cancel"),
click: function() {
if (onCancel) { onCancel() };
RED.tray.close();
}
},
@ -80,7 +82,8 @@
text: RED._("common.label.done"),
class: "primary",
click: function() {
onComplete(expressionEditor.getValue(),expressionEditor.getCursorPosition());
expressionEditor.saveView();
if (onComplete) { onComplete(expressionEditor.getValue(),expressionEditor.getCursorPosition(), expressionEditor); }
RED.tray.close();
}
}
@ -99,6 +102,8 @@
expressionEditor = RED.editor.createEditor({
id: 'red-ui-editor-type-markdown',
value: value,
stateId: options.stateId,
focus: true,
mode:"ace/mode/markdown",
expandable: false
});
@ -143,17 +148,18 @@
});
RED.popover.tooltip($("#node-btn-markdown-preview"), RED._("markdownEditor.toggle-preview"));
if (options.cursor) {
if (options.cursor && !expressionEditor._initState) {
expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false);
}
dialogForm.i18n();
},
close: function() {
expressionEditor.destroy();
if (options.onclose) {
options.onclose();
}
expressionEditor.destroy();
delete expressionEditor;
},
show: function() {}
}

View File

@ -8,7 +8,6 @@
create: function(container) {
this.editor = buildDescriptionForm(container,node);
RED.e = this.editor;
},
resize: function(size) {
this.editor.resize();
@ -58,11 +57,9 @@
var nodeInfoEditor = RED.editor.createEditor({
id: editorId,
mode: 'ace/mode/markdown',
value: ""
stateId: RED.editor.generateViewStateId("node", node, "nodeinfo",),
value: node.info || ""
});
if (node.info) {
nodeInfoEditor.getSession().setValue(node.info, -1);
}
node.infoEditor = nodeInfoEditor;
return nodeInfoEditor;
}

View File

@ -19,6 +19,7 @@
this.tabflowEditor = RED.editor.createEditor({
id: 'node-input-info',
mode: 'ace/mode/markdown',
stateId: options.stateId,
value: ""
});

View File

@ -21,6 +21,7 @@
var definition = {
show: function(options) {
var value = options.value;
var onCancel = options.cancel;
var onComplete = options.complete;
var type = "_text"
if ($("script[data-template-name='"+type+"']").length === 0) {
@ -28,16 +29,16 @@
}
RED.view.state(RED.state.EDITING);
var expressionEditor;
var changeTimer;
var trayOptions = {
title: options.title,
focusElement: options.focusElement,
width: options.width||"inherit",
buttons: [
{
id: "node-dialog-cancel",
text: RED._("common.label.cancel"),
click: function() {
if(onCancel) { onCancel() };
RED.tray.close();
}
},
@ -46,7 +47,8 @@
text: RED._("common.label.done"),
class: "primary",
click: function() {
onComplete(expressionEditor.getValue(),expressionEditor.getCursorPosition());
expressionEditor.saveView();
if (onComplete) { onComplete(expressionEditor.getValue(),expressionEditor.getCursorPosition(),expressionEditor);}
RED.tray.close();
}
}
@ -55,31 +57,28 @@
var rows = $("#dialog-form>div:not(.node-text-editor-row)");
var editorRow = $("#dialog-form>div.node-text-editor-row");
var height = $("#dialog-form").height();
// for (var i=0;i<rows.size();i++) {
// height -= $(rows[i]).outerHeight(true);
// }
// height -= (parseInt($("#dialog-form").css("marginTop"))+parseInt($("#dialog-form").css("marginBottom")));
$(".node-text-editor").css("height",height+"px");
expressionEditor.resize();
},
open: function(tray) {
var trayBody = tray.find('.red-ui-tray-body');
var dialogForm = RED.editor.buildEditForm(tray.find('.red-ui-tray-body'),'dialog-form',type,'editor');
expressionEditor = RED.editor.createEditor({
id: 'node-input-text',
value: "",
mode:"ace/mode/"+(options.mode||"text")
value: value||"",
stateId: options.stateId,
mode:"ace/mode/"+(options.mode||"text"),
focus: true,
});
expressionEditor.getSession().setValue(value||"",-1);
if (options.cursor) {
if (options.cursor && !expressionEditor._initState) {
expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false);
}
},
close: function() {
expressionEditor.destroy();
if (options.onclose) {
options.onclose();
}
expressionEditor.destroy();
delete expressionEditor;
},
show: function() {}
}

View File

@ -120,6 +120,7 @@ RED.projects.settings = (function() {
title: RED._('sidebar.project.editDescription'),
header: $('<span><i class="fa fa-book"></i> README.md</span>'),
value: activeProject.description,
stateId: "sidebar.project.editDescription",
complete: function(v) {
container.empty();
var spinner = utils.addSpinnerOverlay(container);

View File

@ -169,7 +169,13 @@
raiseTrayZ();
handleWindowResize();//cause call to monaco layout
},200);
body.find(":focusable:first").trigger("focus");
if(!options.hasOwnProperty("focusElement")) {
//focusElement is not inside options - default to focusing 1st
body.find(":focusable:first").trigger("focus");
} else if(!options.focusElement === false) {
//focusElement IS specified, focus that instead (if not false)
$(options.focusElement).trigger("focus");
}
},150);
el.css({right:0});

View File

@ -399,11 +399,19 @@
$("#func-tabs-content").children().hide();
$("#" + tab.id).show();
let editor = $("#" + tab.id).find('.monaco-editor').first();
if(editor.length) {
if(editor.length) {
if(that.editor.nodered && that.editor.type == "monaco") {
that.editor.nodered.refreshModuleLibs(getLibsList());
}
RED.tray.resize();
//auto focus editor on tab switch
if (that.initEditor.getDomNode() == editor[0]) {
that.initEditor.focus();
} else if (that.editor.getDomNode() == editor[0]) {
that.editor.focus();
} else if (that.finalizeEditor.getDomNode() == editor[0]) {
that.finalizeEditor.focus();
}
}
}
});
@ -438,11 +446,13 @@
}
});
var buildEditor = function(id, value, defaultValue, extraLibs) {
var buildEditor = function(id, stateId, focus, value, defaultValue, extraLibs) {
var editor = RED.editor.createEditor({
id: id,
mode: 'ace/mode/nrjavascript',
value: value || defaultValue || "",
stateId: stateId,
focus: true,
globals: {
msg:true,
context:true,
@ -462,11 +472,12 @@
if (defaultValue && value === "") {
editor.moveCursorTo(defaultValue.split("\n").length - 1, 0);
}
editor.__stateId = stateId;
return editor;
}
this.initEditor = buildEditor('node-input-init-editor',$("#node-input-initialize").val(),RED._("node-red:function.text.initialize"))
this.editor = buildEditor('node-input-func-editor',$("#node-input-func").val(), undefined, that.libs || [])
this.finalizeEditor = buildEditor('node-input-finalize-editor',$("#node-input-finalize").val(),RED._("node-red:function.text.finalize"))
this.initEditor = buildEditor('node-input-init-editor', this.id + "/" + "initEditor", false, $("#node-input-initialize").val(), RED._("node-red:function.text.initialize"))
this.editor = buildEditor('node-input-func-editor', this.id + "/" + "editor", true, $("#node-input-func").val(), undefined, that.libs || [])
this.finalizeEditor = buildEditor('node-input-finalize-editor', this.id + "/" + "finalizeEditor", false, $("#node-input-finalize").val(), RED._("node-red:function.text.finalize"))
RED.library.create({
url:"functions", // where to get the data from
@ -505,28 +516,33 @@
],
ext:"js"
});
this.editor.focus();
var expandButtonClickHandler = function(editor) {
return function(e) {
return function (e) {
e.preventDefault();
var value = editor.getValue();
editor.saveView(`inside function-expandButtonClickHandler ${editor.__stateId}`);
var extraLibs = that.libs || [];
RED.editor.editJavaScript({
value: value,
width: "Infinity",
cursor: editor.getCursorPosition(),
stateId: editor.__stateId,
mode: "ace/mode/nrjavascript",
complete: function(v,cursor) {
editor.setValue(v, -1);
editor.gotoLine(cursor.row+1,cursor.column,false);
setTimeout(function() {
focus: true,
cancel: function () {
setTimeout(function () {
editor.focus();
},300);
}, 250);
},
complete: function (v, cursor) {
editor.setValue(v, -1);
setTimeout(function () {
editor.restoreView();
editor.focus();
}, 250);
},
extraLibs: extraLibs
})
});
}
}
$("#node-init-expand-js").on("click", expandButtonClickHandler(this.initEditor));

View File

@ -18,7 +18,7 @@
<option value="handlebars">mustache</option>
<option value="html">HTML</option>
<option value="json">JSON</option>
<option value="javascript">Javascript</option>
<option value="javascript">JavaScript</option>
<option value="css">CSS</option>
<option value="markdown">Markdown</option>
<option value="python">Python</option>
@ -73,7 +73,8 @@
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
var that = this;
const that = this;
const stateId = RED.editor.generateViewStateId("node", this, "");
if (!this.field) {
this.field = 'payload';
$("#node-input-field").val("payload");
@ -90,10 +91,10 @@
types: ['msg','flow','global'],
typeField: $("#node-input-fieldType")
});
this.editor = RED.editor.createEditor({
id: 'node-input-template-editor',
mode: 'ace/mode/html',
stateId: stateId,
value: $("#node-input-template").val()
});
RED.library.create({
@ -103,7 +104,6 @@
fields:['name','format','output','syntax'],
ext: "txt"
});
this.editor.focus();
$("#node-input-format").on("change", function() {
var mod = "ace/mode/"+$("#node-input-format").val();
@ -113,20 +113,22 @@
});
});
RED.popover.tooltip($("#node-template-expand-editor"), RED._("node-red:common.label.expand"));
$("#node-template-expand-editor").on("click", function(e) {
$("#node-template-expand-editor").on("click", function (e) {
e.preventDefault();
var value = that.editor.getValue();
const value = that.editor.getValue();
that.editor.saveView();
RED.editor.editText({
mode: $("#node-input-format").val(),
value: value,
stateId: stateId,
width: "Infinity",
cursor: that.editor.getCursorPosition(),
complete: function(v,cursor) {
focus: true,
complete: function (v, cursor) {
that.editor.setValue(v, -1);
that.editor.gotoLine(cursor.row+1,cursor.column,false);
setTimeout(function() {
setTimeout(function () {
that.editor.restoreView();
that.editor.focus();
},300);
}, 250);
}
})
})