From 3d59ece3a206a5bbe8b22566c31a93e671635019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?bahad=C4=B1r?= Date: Sun, 16 Oct 2022 23:37:48 +0300 Subject: [PATCH] Back/Frovard Navigation by `window.history` feature added to editor. --- .../editor-client/locales/en-US/editor.json | 6 + .../@node-red/editor-client/src/js/red.js | 159 ++++++++++++------ .../editor-client/src/js/ui/editor.js | 5 + .../editor-client/src/js/ui/tab-config.js | 12 +- .../@node-red/editor-client/src/js/ui/tray.js | 2 + .../editor-client/src/js/ui/userSettings.js | 8 + .../editor-client/src/js/ui/workspaces.js | 13 +- 7 files changed, 142 insertions(+), 63 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json index 1cc571200..32d995762 100755 --- a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json @@ -88,6 +88,12 @@ "language": "Language", "browserDefault": "Browser default" }, + "navigationHistory":{ + "title":"Back/Forward Navigation", + "trackNodes": "Enable node selection tracking", + "trackFlows": "Enable flow selection tracking", + "trackEditor": "Enable editor selection tracking" + }, "sidebar": { "show": "Show sidebar" }, diff --git a/packages/node_modules/@node-red/editor-client/src/js/red.js b/packages/node_modules/@node-red/editor-client/src/js/red.js index 129c46799..7346c1d37 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/red.js +++ b/packages/node_modules/@node-red/editor-client/src/js/red.js @@ -242,59 +242,12 @@ var RED = (function() { url: 'flows', success: function(nodes) { if (nodes) { - var currentHash = window.location.hash; RED.nodes.version(nodes.rev); loader.reportProgress(RED._("event.importFlows"),90 ) try { RED.nodes.import(nodes.flows); RED.nodes.dirty(false); RED.view.redraw(true); - if (/^#(flow|node|group)\/.+$/.test(currentHash)) { - const hashParts = currentHash.split('/') - const showEditDialog = hashParts.length > 2 && hashParts[2] === 'edit' - if (hashParts[0] === '#flow') { - RED.workspaces.show(hashParts[1], true); - if (showEditDialog) { - RED.workspaces.edit() - } - } else if (hashParts[0] === '#node') { - const nodeToShow = RED.nodes.node(hashParts[1]) - if (nodeToShow) { - setTimeout(() => { - RED.view.reveal(nodeToShow.id) - window.location.hash = currentHash - if (showEditDialog) { - RED.editor.edit(nodeToShow) - } - }, 50) - } - } else if (hashParts[0] === '#group') { - const nodeToShow = RED.nodes.group(hashParts[1]) - if (nodeToShow) { - RED.view.reveal(nodeToShow.id) - window.location.hash = currentHash - if (showEditDialog) { - RED.editor.editGroup(nodeToShow) - } - } - } - } - if (RED.workspaces.count() > 0) { - const hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}"); - const workspaces = RED.nodes.getWorkspaceOrder(); - if (RED.workspaces.active() === 0) { - for (let index = 0; index < workspaces.length; index++) { - const ws = workspaces[index]; - if (!hiddenTabs[ws]) { - RED.workspaces.show(ws); - break; - } - } - } - if (RED.workspaces.active() === 0) { - RED.workspaces.show(workspaces[0]); - } - } } catch(err) { console.warn(err); RED.notify( @@ -615,6 +568,7 @@ var RED = (function() { if (showProjectWelcome) { RED.projects.showStartup(); } + initNavigation() }); },100); } @@ -737,6 +691,117 @@ var RED = (function() { } + function initNavigation() { + let lastNavigated + let state = "" + function navigateToHash(currentHash) { + console.log("Navigate", currentHash) + if (currentHash === "") { return revealAndShowEditDialog("fallback") } + if (!/^#(flow|node|group|junction)\/.+$/.test(currentHash)) { return } + const [type, id, edit] = currentHash.split('/') + RED.tray.close(() => revealAndShowEditDialog(type, id, edit === 'edit')); + } + function cancelFlash(type, id) { + if(type === "#flow"){ + clearInterval($("#red-ui-tab-" + id).removeClass("highlighted")[0].__flashTimer) + } else if (["#node", "#junction", "#group"].includes(type)){ + const nodeToCancel = RED.nodes[type.substring(1)](id); + if(nodeToCancel && nodeToCancel.type != "group" && nodeToCancel._def && nodeToCancel._def.category === "config"){ + let configNode = $(".red-ui-palette-node_id_" + id) + clearInterval(configNode[0].__flashTimer) + configNode.children("div").removeClass("highlighted") + } + else if(nodeToCancel){ + clearInterval(nodeToCancel.__flashTimer) + nodeToCancel.highlighted = false + RED.view.redraw() + } + } + } + + function revealAndShowEditDialog(type, id, showEditDialog) { + state = "Nav"; + if(lastNavigated){cancelFlash(lastNavigated.type, lastNavigated.id)} + + lastNavigated = {type,id} + if (type === '#flow') { + RED.workspaces.show(id, true, false, true); + if (showEditDialog) { + RED.workspaces.edit(); + } + } else if (["#node", "#junction", "#group"].includes(type)) { + const nodeToShow = RED.nodes[type.substring(1)](id); + if (nodeToShow) { + RED.view.select(nodeToShow.id); + RED.view.reveal(nodeToShow.id); + if (showEditDialog && type != "#group") { + RED.editor.edit(nodeToShow); + } else if (showEditDialog) { + RED.editor.editGroup(nodeToShow); + } + } + } + if (type === "fallback" || RED.workspaces.active() === 0) { + fallbackWorkspace(); + } + state = "Idle" + } + + function fallbackWorkspace() { + if (RED.workspaces.count() > 0) { + let workspaces = RED.nodes.getWorkspaceOrder(); + let fallbackws = workspaces.find(x => !RED.workspaces.isHidden(x)); + RED.workspaces.show(fallbackws, true); + } + } + + function setLocationHash(node, edit) { + if(state != "Idle") {return} + state = "Log" + let newHash = "" + if (["tab", "flow"].includes(node.type) && RED.settings.get("editor.view.view-nav-track-flows")) { + newHash = "#flow/" + node.id + } + else if (["group", "junction"].includes(node.type) && RED.settings.get("editor.view.view-nav-track-nodes")) { + newHash = "#" + node.type + "/" + node.id + } + else if (RED.settings.get("editor.view.view-nav-track-nodes")) { + newHash = "#node/" + node.id + } + if (edit) { + newHash += "/edit" + } + console.log("set hash", newHash) + window.location.hash = newHash + } + + navigateToHash(window.location.hash) + + window.addEventListener('hashchange', function (e) { + if(state != "Idle") { + state = "Idle" + return + } + navigateToHash(window.location.hash) + }, false); + + RED.events.on("view:selection-changed", function (selection) { + if (selection.nodes && selection.nodes.length === 1) { + setLocationHash(selection.nodes[0]) + } + }); + RED.events.on("workspace:change",function(event){ + setLocationHash({id:event.workspace, type:"flow"}) + }); + + RED.events.on("tab-config:selection-changed", function (node) { + setLocationHash(node) + }); + RED.events.on("editor:editing", function(node){ + setLocationHash(node, true) + }); + } + function loadEditor() { RED.workspaces.init(); RED.statusBar.init(); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js index fb4c200f5..5c7ca7302 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js @@ -1125,6 +1125,7 @@ RED.editor = (function() { }); } + RED.events.emit("editor:editing",editing_node); RED.tray.show(trayOptions); } /** @@ -1512,6 +1513,7 @@ RED.editor = (function() { }); } + RED.events.emit("editor:editing",editing_config_node); RED.tray.show(trayOptions); } @@ -1682,6 +1684,7 @@ RED.editor = (function() { show: function() { } } + RED.events.emit("editor:editing",editing_node); RED.tray.show(trayOptions); } @@ -1795,6 +1798,7 @@ RED.editor = (function() { if (editTrayWidthCache.hasOwnProperty('group')) { trayOptions.width = editTrayWidthCache['group']; } + RED.events.emit("editor:editing",editing_node); RED.tray.show(trayOptions); } @@ -1929,6 +1933,7 @@ RED.editor = (function() { } } } + RED.events.emit("editor:editing", workspace); RED.tray.show(trayOptions); } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-config.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-config.js index bb956daa3..4f0f24102 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-config.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-config.js @@ -16,7 +16,6 @@ RED.sidebar.config = (function() { let flashingConfigNode; - let flashingConfigNodeTimer; var content = document.createElement("div"); content.className = "red-ui-sidebar-node-config"; @@ -180,6 +179,7 @@ RED.sidebar.config = (function() { $(this).addClass("selected"); } RED.sidebar.info.refresh(node); + RED.events.emit("tab-config:selection-changed",node) }); nodeDiv.on('dblclick',function(e) { e.stopPropagation(); @@ -357,24 +357,22 @@ RED.sidebar.config = (function() { function flashConfigNode(el) { if(flashingConfigNode && flashingConfigNode.length) { //cancel current flashing node before flashing new node - clearInterval(flashingConfigNodeTimer); - flashingConfigNodeTimer = null; + clearInterval(el[0].__flashTimer); flashingConfigNode.children("div").removeClass('highlighted'); flashingConfigNode = null; } if(!el || !el.children("div").length) { return; } - flashingConfigNodeTimer = setInterval(function(flashEndTime) { + el[0].__flashTimer = setInterval(function(flashEndTime) { if (flashEndTime >= Date.now()) { const highlighted = el.children("div").hasClass("highlighted"); el.children("div").toggleClass('highlighted', !highlighted) } else { - clearInterval(flashingConfigNodeTimer); - flashingConfigNodeTimer = null; + clearInterval(el[0].__flashTimer); flashingConfigNode = null; el.children("div").removeClass('highlighted'); } - }, 100, Date.now() + 2200); + }, 100, Date.now() + 2200, el); flashingConfigNode = el; el.children("div").addClass('highlighted'); } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tray.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tray.js index 0ea6d6044..44089cdf6 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/tray.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tray.js @@ -335,6 +335,8 @@ stack[stack.length-1].tray.css("z-index", "auto"); } },250) + }else if (done){ + done() } } } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/userSettings.js b/packages/node_modules/@node-red/editor-client/src/js/ui/userSettings.js index 1b61f396e..77bd93e3f 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/userSettings.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/userSettings.js @@ -143,6 +143,14 @@ RED.userSettings = (function() { {setting:"view-node-show-label",label:"menu.label.showNodeLabelDefault",default: true, toggle:true} ] }, + { + title: "menu.label.navigationHistory.title", + options: [ + {setting:"view-nav-track-nodes",label:"menu.label.navigationHistory.trackNodes",default: true, toggle:true}, + {setting:"view-nav-track-flows",label:"menu.label.navigationHistory.trackFlows",default: true, toggle:true}, + {setting:"view-nav-track-edit",label:"menu.label.navigationHistory.trackEditor",default: false, toggle:true} + ] + }, { title: "menu.label.other", options: [ diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js b/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js index ae38f2c4d..07b215ad1 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js @@ -25,7 +25,6 @@ RED.workspaces = (function() { var viewStackPos = 0; let flashingTab; - let flashingTabTimer; function addToViewStack(id) { if (viewStackPos !== viewStack.length) { @@ -136,12 +135,10 @@ RED.workspaces = (function() { if (tab) { $("#red-ui-workspace-chart").show(); activeWorkspace = tab.id; - window.location.hash = 'flow/'+tab.id; $("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!tab.disabled); } else { $("#red-ui-workspace-chart").hide(); activeWorkspace = 0; - window.location.hash = ''; } event.workspace = activeWorkspace; RED.events.emit("workspace:change",event); @@ -550,25 +547,23 @@ RED.workspaces = (function() { function flashTab(tabId) { if(flashingTab && flashingTab.length) { //cancel current flashing node before flashing new node - clearInterval(flashingTabTimer); - flashingTabTimer = null; + clearInterval(flashingTab.__flashTimer); flashingTab.removeClass('highlighted'); flashingTab = null; } let tab = $("#red-ui-tab-" + tabId); if(!tab || !tab.length) { return; } - flashingTabTimer = setInterval(function(flashEndTime) { + tab[0].__flashTimer = setInterval(function(flashEndTime) { if (flashEndTime >= Date.now()) { const highlighted = tab.hasClass("highlighted"); tab.toggleClass('highlighted', !highlighted) } else { - clearInterval(flashingTabTimer); - flashingTabTimer = null; + clearInterval(tab[0].__flashTimer); flashingTab = null; tab.removeClass('highlighted'); } - }, 100, Date.now() + 2200); + }, 100, Date.now() + 2200, tab); flashingTab = tab; tab.addClass('highlighted'); }