From 5d86f7b6ba9f66558b3674891bad22d2121c0a11 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 26 Jun 2018 23:34:32 +0100 Subject: [PATCH] Refresh context sidebar tab based on selection --- editor/js/ui/tab-context.js | 232 ++++++++++++++++++++++++++++++++++- editor/sass/style.scss | 1 + editor/sass/tab-context.scss | 41 +++++++ red/api/admin/context.js | 61 +++++++++ red/api/admin/index.js | 8 ++ red/runtime/nodes/context.js | 1 + red/runtime/nodes/index.js | 2 + 7 files changed, 343 insertions(+), 3 deletions(-) create mode 100644 editor/sass/tab-context.scss create mode 100644 red/api/admin/context.js diff --git a/editor/js/ui/tab-context.js b/editor/js/ui/tab-context.js index 48176d624..863dacc76 100644 --- a/editor/js/ui/tab-context.js +++ b/editor/js/ui/tab-context.js @@ -14,11 +14,110 @@ * limitations under the License. **/ RED.sidebar.context = (function() { + var content; + var sections; + + var localCache = {}; + + + var nodeSection; + // var subflowSection; + var flowSection; + var globalSection; + + var currentNode; + var currentFlow; function init() { - content = document.createElement("div"); - content.className = "sidebar-node-info" + content = $("
").css({"position":"relative","height":"100%"}); + content.className = "sidebar-context" + // var toolbar = $('').appendTo(content); + + var footerToolbar = $('
'+ + // ' ' + + '
'); + + + + var stackContainer = $("
",{class:"sidebar-context-stack"}).appendTo(content); + sections = RED.stack.create({ + container: stackContainer + }); + + nodeSection = sections.add({ + title: "Node", + collapsible: true, + // onexpand: function() { + // updateNode(currentNode,true); + // } + }); + nodeSection.expand(); + nodeSection.content.css({height:"100%"}); + var table = $('
').appendTo(nodeSection.content); + nodeSection.table = $('').appendTo(table); + var bg = $('
').appendTo(nodeSection.header); + $('') + .appendTo(bg) + .click(function(evt) { + evt.stopPropagation(); + evt.preventDefault(); + updateNode(currentNode, true); + }) + + // subflowSection = sections.add({ + // title: "Subflow", + // collapsible: true + // }); + // subflowSection.expand(); + // subflowSection.content.css({height:"100%"}); + // bg = $('
').appendTo(subflowSection.header); + // $('') + // .appendTo(bg) + // .click(function(evt) { + // evt.stopPropagation(); + // evt.preventDefault(); + // }) + // + // subflowSection.container.hide(); + + flowSection = sections.add({ + title: "Flow", + collapsible: true + }); + flowSection.expand(); + flowSection.content.css({height:"100%"}); + var table = $('
').appendTo(flowSection.content); + flowSection.table = $('').appendTo(table); + bg = $('
').appendTo(flowSection.header); + $('') + .appendTo(bg) + .click(function(evt) { + evt.stopPropagation(); + evt.preventDefault(); + updateFlow(currentFlow); + }) + + globalSection = sections.add({ + title: "Global", + collapsible: true + }); + globalSection.expand(); + globalSection.content.css({height:"100%"}); + var table = $('
').appendTo(globalSection.content); + globalSection.table = $('').appendTo(table); + + bg = $('
').appendTo(globalSection.header); + $('') + .appendTo(bg) + .click(function(evt) { + evt.stopPropagation(); + evt.preventDefault(); + updateEntry(globalSection,"context/global","global"); + }) + RED.actions.add("core:show-context-tab",show); @@ -28,10 +127,137 @@ RED.sidebar.context = (function() { name: RED._("sidebar.context.name"), iconClass: "fa fa-database", content: content, - pinned: false, + toolbar: footerToolbar, + // pinned: true, enableOnEdit: false }); + + // var toggleLiveButton = $("#sidebar-context-toggle-live"); + // toggleLiveButton.click(function(evt) { + // evt.preventDefault(); + // if ($(this).hasClass("selected")) { + // $(this).removeClass("selected"); + // $(this).find("i").removeClass("fa-pause"); + // $(this).find("i").addClass("fa-play"); + // } else { + // $(this).addClass("selected"); + // $(this).find("i").removeClass("fa-play"); + // $(this).find("i").addClass("fa-pause"); + // } + // }); + // RED.popover.tooltip(toggleLiveButton, function() { + // if (toggleLiveButton.hasClass("selected")) { + // return "Pause live updates" + // } else { + // return "Start live updates" + // } + // }); + + + RED.events.on("view:selection-changed", function(event) { + var selectedNode = event.nodes && event.nodes.length === 1 && event.nodes[0]; + updateNode(selectedNode); + }) + + RED.events.on("workspace:change", function(event) { + updateFlow(RED.nodes.workspace(event.workspace)); + }) + + updateEntry(globalSection,"context/global","global"); } + + function updateNode(node,force) { + currentNode = node; + if (force) { + if (node) { + updateEntry(nodeSection,"context/node/"+node.id,node.id); + // if (/^subflow:/.test(node.type)) { + // subflowSection.container.show(); + // updateEntry(subflowSection,"context/flow/"+node.id,node.id); + // } else { + // subflowSection.container.hide(); + // } + } else { + // subflowSection.container.hide(); + updateEntry(nodeSection) + } + } else { + $(nodeSection.table).empty(); + if (node) { + $('refresh to load').appendTo(nodeSection.table); + + } else { + $('none selected').appendTo(nodeSection.table); + } + + } + } + function updateFlow(flow) { + currentFlow = flow; + if (flow) { + updateEntry(flowSection,"context/flow/"+flow.id,flow.id); + } else { + updateEntry(flowSection) + } + } + + function refreshEntry(section,baseUrl,id) { + var container = section.table; + + $.getJSON(baseUrl, function(data) { + $(container).empty(); + var propRow; + + var keys = Object.keys(data); + keys.sort(); + var l = keys.length; + for (var i = 0; i < l; i++) { + var k = keys[i]; + propRow = $('').appendTo(container); + var obj = $(propRow.children()[0]); + obj.text(k); + var tools = $('').appendTo(obj); + var refreshItem = $('').appendTo(tools).click(function(e) { + e.preventDefault(); + e.stopPropagation(); + $.getJSON(baseUrl+"/"+k, function(data) { + $(propRow.children()[1]).empty(); + var payload = data.msg; + var format = data.format; + payload = RED.utils.decodeObject(payload,format); + RED.utils.createObjectElement(payload, { + typeHint: data.format, + sourceId: id+"."+k + }).appendTo(propRow.children()[1]); + }) + }); + + + var payload = data[k].msg; + var format = data[k].format; + payload = RED.utils.decodeObject(payload,format); + RED.utils.createObjectElement(payload, { + typeHint: data[k].format, + sourceId: id+"."+k + }).appendTo(propRow.children()[1]); + } + if (l === 0) { + $('empty').appendTo(container); + } + }); + } + function updateEntry(section,baseUrl,id) { + var container = section.table; + if (id) { + refreshEntry(section,baseUrl,id); + } else { + $(container).empty(); + $('none selected').appendTo(container); + } + } + + + function show() { RED.sidebar.show("context"); } diff --git a/editor/sass/style.scss b/editor/sass/style.scss index 5d536c742..b160959c8 100644 --- a/editor/sass/style.scss +++ b/editor/sass/style.scss @@ -40,6 +40,7 @@ @import "panels"; @import "tabs"; @import "tab-config"; +@import "tab-context"; @import "tab-info"; @import "popover"; @import "flow"; diff --git a/editor/sass/tab-context.scss b/editor/sass/tab-context.scss new file mode 100644 index 000000000..917b0dc48 --- /dev/null +++ b/editor/sass/tab-context.scss @@ -0,0 +1,41 @@ +/** + * Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +.sidebar-context-stack { + position: absolute; + top: 0; // with toolbar: 43px; + bottom: 0; + left: 0; + right: 0; + overflow-y: scroll; + + .palette-category { + &:not(.palette-category-expanded) button { + display: none; + } + } +} + +.sidebar-context-property { + position: relative; + .debug-message-tools { + right: 3px; + display: none; + } + &:hover .debug-message-tools { + display: inline-block; + } +} diff --git a/red/api/admin/context.js b/red/api/admin/context.js new file mode 100644 index 000000000..37c6462cb --- /dev/null +++ b/red/api/admin/context.js @@ -0,0 +1,61 @@ +/** + * Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +var log; +var redNodes; +var util; + +module.exports = { + init: function(runtime) { + redNodes = runtime.nodes; + log = runtime.log; + util = runtime.util; + }, + + get: function(req,res) { + var scope = req.params.scope; + var id = req.params.id; + var key = req.params[0]; + var result = {}; + var ctx; + if (scope === 'global') { + ctx = redNodes.getContext('global'); + } else if (scope === 'flow') { + ctx = redNodes.getContext(id); + } else if (scope === 'node') { + var node = redNodes.getNode(id); + if (node) { + ctx = node.context(); + } + } + if (ctx) { + if (key) { + result = util.encodeObject({msg:ctx.get(key)}); + } else { + var keys = ctx.keys(); + + var i = 0; + var l = keys.length; + while(i < l) { + var k = keys[i]; + result[k] = util.encodeObject({msg:ctx.get(k)}); + i++; + } + } + } + res.json(result); + } +} diff --git a/red/api/admin/index.js b/red/api/admin/index.js index 57c79eb62..15e56aecf 100644 --- a/red/api/admin/index.js +++ b/red/api/admin/index.js @@ -19,6 +19,7 @@ var express = require("express"); var nodes = require("./nodes"); var flows = require("./flows"); var flow = require("./flow"); +var context = require("./context"); var auth = require("../auth"); var apiUtil = require("../util"); @@ -28,6 +29,7 @@ module.exports = { flows.init(runtime); flow.init(runtime); nodes.init(runtime); + context.init(runtime); var needsPermission = auth.needsPermission; @@ -52,6 +54,12 @@ module.exports = { adminApp.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,needsPermission("nodes.read"),nodes.getSet,apiUtil.errorHandler); adminApp.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,needsPermission("nodes.write"),nodes.putSet,apiUtil.errorHandler); + // Context + adminApp.get("/context/:scope(global)",needsPermission("context.read"),context.get,apiUtil.errorHandler); + adminApp.get("/context/:scope(global)/*",needsPermission("context.read"),context.get,apiUtil.errorHandler); + adminApp.get("/context/:scope(node|flow)/:id",needsPermission("context.read"),context.get,apiUtil.errorHandler); + adminApp.get("/context/:scope(node|flow)/:id/*",needsPermission("context.read"),context.get,apiUtil.errorHandler); + return adminApp; } } diff --git a/red/runtime/nodes/context.js b/red/runtime/nodes/context.js index e71a1eaed..2d6e7ba32 100644 --- a/red/runtime/nodes/context.js +++ b/red/runtime/nodes/context.js @@ -84,6 +84,7 @@ function clean(flowConfig) { module.exports = { init: function(settings) { globalContext = createContext("global",settings.functionGlobalContext || {}); + contexts['global'] = globalContext; }, get: getContext, delete: deleteContext, diff --git a/red/runtime/nodes/index.js b/red/runtime/nodes/index.js index b2f8ff3b5..be84130ef 100644 --- a/red/runtime/nodes/index.js +++ b/red/runtime/nodes/index.js @@ -167,6 +167,8 @@ module.exports = { createNode: createNode, getNode: flows.get, eachNode: flows.eachNode, + getContext: context.get, + paletteEditorEnabled: registry.paletteEditorEnabled, installModule: installModule,