1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

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

View File

@ -796,6 +796,7 @@ RED.editor = (function() {
if (buildingEditDialog) { return } if (buildingEditDialog) { return }
buildingEditDialog = true; buildingEditDialog = true;
var editing_node = node; var editing_node = node;
var removeInfoEditorOnClose = false;
var skipInfoRefreshOnClose = false; var skipInfoRefreshOnClose = false;
var activeEditPanes = []; var activeEditPanes = [];
@ -991,6 +992,14 @@ RED.editor = (function() {
} }
if (!node._def.defaults || !node._def.defaults.hasOwnProperty('info')) { if (!node._def.defaults || !node._def.defaults.hasOwnProperty('info')) {
nodeEditPanes.push('editor-tab-description'); 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"); nodeEditPanes.push("editor-tab-appearance");
@ -1006,9 +1015,18 @@ RED.editor = (function() {
if (RED.view.state() != RED.state.IMPORT_DRAGGING) { if (RED.view.state() != RED.state.IMPORT_DRAGGING) {
RED.view.state(RED.state.DEFAULT); RED.view.state(RED.state.DEFAULT);
} }
if (editing_node && !skipInfoRefreshOnClose) { 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.sidebar.info.refresh(editing_node);
} }
}
RED.workspaces.refresh(); RED.workspaces.refresh();
activeEditPanes.forEach(function(pane) { activeEditPanes.forEach(function(pane) {
@ -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 { return {
init: function() { init: function() {
if(window.ace) { window.ace.config.set('basePath', 'vendor/ace'); } if(window.ace) { window.ace.config.set('basePath', 'vendor/ace'); }
@ -1883,6 +1943,7 @@ RED.editor = (function() {
}); });
RED.editor.codeEditor.init(); RED.editor.codeEditor.init();
}, },
generateViewStateId: generateViewStateId,
edit: showEditDialog, edit: showEditDialog,
editConfig: showEditConfigNodeDialog, editConfig: showEditConfigNodeDialog,
editFlow: showEditFlowDialog, editFlow: showEditFlowDialog,

View File

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

View File

@ -80,6 +80,9 @@ RED.editor.codeEditor.ace = (function() {
} }
},100); },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') { if (options.mode === 'ace/mode/markdown') {
$(el).addClass("red-ui-editor-text-container-toolbar"); $(el).addClass("red-ui-editor-text-container-toolbar");
editor.toolbar = RED.editor.customEditTypes['_markdown'].buildToolbar(toolbarRow,editor); editor.toolbar = RED.editor.customEditTypes['_markdown'].buildToolbar(toolbarRow,editor);
@ -92,11 +95,15 @@ RED.editor.codeEditor.ace = (function() {
RED.editor.editMarkdown({ RED.editor.editMarkdown({
value: value, value: value,
width: "Infinity", width: "Infinity",
cursor: editor.getCursorPosition(), stateId: options.stateId,
focus: true,
cancel: function () {
editor.focus();
},
complete: function(v,cursor) { complete: function(v,cursor) {
editor.setValue(v, -1); editor.setValue(v, -1);
editor.gotoLine(cursor.row+1,cursor.column,false);
setTimeout(function() { setTimeout(function() {
editor.restoreView();
editor.focus(); editor.focus();
},300); },300);
} }
@ -117,11 +124,56 @@ RED.editor.codeEditor.ace = (function() {
editor._destroy = editor.destroy; editor._destroy = editor.destroy;
editor.destroy = function() { editor.destroy = function() {
try { try {
editor.saveView();
editor._initState = null;
this._destroy(); this._destroy();
} catch (e) { } } catch (e) { }
$(el).remove(); $(el).remove();
$(toolbarRow).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; editor.type = type;
return editor; return editor;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,7 +8,6 @@
create: function(container) { create: function(container) {
this.editor = buildDescriptionForm(container,node); this.editor = buildDescriptionForm(container,node);
RED.e = this.editor;
}, },
resize: function(size) { resize: function(size) {
this.editor.resize(); this.editor.resize();
@ -58,11 +57,9 @@
var nodeInfoEditor = RED.editor.createEditor({ var nodeInfoEditor = RED.editor.createEditor({
id: editorId, id: editorId,
mode: 'ace/mode/markdown', 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; node.infoEditor = nodeInfoEditor;
return nodeInfoEditor; return nodeInfoEditor;
} }

View File

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

View File

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

View File

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

View File

@ -169,7 +169,13 @@
raiseTrayZ(); raiseTrayZ();
handleWindowResize();//cause call to monaco layout handleWindowResize();//cause call to monaco layout
},200); },200);
if(!options.hasOwnProperty("focusElement")) {
//focusElement is not inside options - default to focusing 1st
body.find(":focusable:first").trigger("focus"); 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); },150);
el.css({right:0}); el.css({right:0});

View File

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

View File

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