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

Add declarative menu structure

This commit is contained in:
Nick O'Leary 2014-08-20 21:58:54 +01:00
parent 47b4ebc92f
commit 7176f3ee2b
10 changed files with 248 additions and 9723 deletions

View File

@ -29,54 +29,15 @@
<link rel="stylesheet" href="style.css">
<head>
<body spellcheck="false">
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container-fluid">
<a class="brand" href="#"><img src="node-red.png"> <span class="red">Node-RED</span> </a>
<div id="header">
<a class="logo" href="#"><img src="node-red.png"> <span class="red">Node-RED</span> </a>
<div class="btn-group pull-right">
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#"><i class="fa fa-bars"></i> <i class="fa fa-caret-down"></i></a>
<ul class="dropdown-menu">
<li><a id="btn-sidebar" tabindex="-1" href="#"><i class="fa fa-check pull-right"></i><i class="fa fa-columns"></i> Sidebar</a></li>
<li class="divider"></li>
<li><a id="btn-node-status" tabindex="-1" href="#"><i class="fa fa-check pull-right"></i><i class="fa fa-info"></i> Node Status</a></li>
<li class="divider"></li>
<li class="dropdown-submenu pull-left"><a tabindex="-1" href="#"><i class="fa fa-sign-in"></i> Import from...</a>
<ul class="dropdown-menu">
<li><a id="btn-import" tabindex="-1" href="#"><i class="fa fa-clipboard"></i> Clipboard...</a></li>
<li id="flow-menu-parent" class="dropdown-submenu pull-left">
<a tabindex="-1" href="#"><i class="fa fa-book"></i> Library</a>
<ul class="dropdown-menu"></ul>
</li>
</ul>
</li>
<li id="li-menu-export" class="dropdown-submenu disabled pull-left"><a tabindex="-1" href="#"><i class="fa fa-sign-out"></i> Export to...</a>
<ul class="dropdown-menu">
<li id="li-menu-export-clipboard" class="disabled"><a id="btn-export-clipboard" tabindex="-1" href="#"><i class="fa fa-clipboard"></i> Clipboard...</a></li>
<li id="li-menu-export-library" class="disabled"><a id="btn-export-library" tabindex="-1" href="#"><i class="fa fa-book"></i> Library...</a></li>
</ul>
</li>
<li class="divider"></li>
<li><a id="btn-config-nodes" tabindex="-1" href="#"><i class="fa fa-th-list"></i> Configuration nodes...</a></li>
<li class="divider"></li>
<li class="dropdown-submenu pull-left"><a tabindex="-1" href="#"><i class="fa fa-th-large"></i> Workspaces</a>
<ul id="workspace-menu-list" class="dropdown-menu">
<li><a id="btn-workspace-add" tabindex="-1" href="#"><i class="fa fa-plus"></i> Add</a></li>
<li><a id="btn-workspace-edit" tabindex="-1" href="#"><i class="fa fa-pencil"></i> Rename</a></li>
<li><a id="btn-workspace-delete" tabindex="-1" href="#"><i class="fa fa-minus"></i> Delete</a></li>
<li class="divider"></li>
</ul>
</li>
<li class="divider"></li>
<li><a id="btn-keyboard-shortcuts" tabindex="-1" href="#"><i class="fa fa-keyboard-o"></i> Keyboard Shortcuts</a></li>
<li><a id="btn-help" tabindex="-1" href="http://node-red.github.io/docs" target="_blank"><i class="fa fa-question"></i> Help...</a></li>
</ul>
<a id="btn-sidemenu" class="btn dropdown-toggle" data-toggle="dropdown" href="#"><i class="fa fa-bars"></i> <i class="fa fa-caret-down"></i></a>
</div>
<div class="btn-group pull-right">
<a id="btn-deploy" class="btn action-deploy disabled" href="#"><i id="btn-icn-deploy" class="fa fa-download"></i> Deploy</a>
</div>
</div>
</div>
</div>
<div id="main-container" class="sidebar-closed">
<div id="palette">
<img src="spin.svg" class="palette-spinner"/>
@ -118,7 +79,7 @@
</div>
<div id="notifications"></div>
<div id="dropTarget"><div>Drop the flow here</div></div>
<div id="dropTarget"><div>Drop the flow here<br/><i class="fa fa-download"></i></div></div>
<div id="dialog" class="hide"><form id="dialog-form" class="form-horizontal"></form></div>
<div id="node-config-dialog" class="hide"><form id="dialog-config-form" class="form-horizontal"></form><div class="form-tips" id="node-config-dialog-user-count"></div></div>
@ -240,7 +201,7 @@
<script type="text/x-red" data-template-name="export-clipboard-dialog">
<div class="form-row">
<label for="node-input-export" style="display: block; width:100%;"><i class="fa fa-share"></i> Nodes:</label>
<label for="node-input-export" style="display: block; width:100%;"><i class="fa fa-clipboard"></i> Nodes:</label>
<textarea readonly style="font-family: monospace; font-size: 12px; background:rgb(226, 229, 255); padding-left: 0.5em;" class="input-block-level" id="node-input-export" rows="5"></textarea>
</div>
<div class="form-tips">
@ -249,18 +210,18 @@
</script>
<script type="text/x-red" data-template-name="export-library-dialog">
<div class="form-row">
<label for="node-input-filename" ><i class="fa fa-tag"></i> Filename:</label>
<label for="node-input-filename" ><i class="fa fa-file"></i> Filename:</label>
<input type="text" id="node-input-filename" placeholder="Filename">
</div>
</script>
<script type="text/x-red" data-template-name="import-dialog">
<div class="form-row">
<label for="node-input-import"><i class="fa fa-share"></i>Nodes:</label>
<textarea style="font-family: monospace; font-size: 12px; background:rgb(226, 229, 255); padding-left: 0.5em;" class="input-block-level" id="node-input-import" rows="5" placeholder="Paste nodes here, or lookup in the library"></textarea>
<label for="node-input-import"><i class="fa fa-clipboard"></i> Nodes:</label>
<textarea style="font-family: monospace; font-size: 12px; background:rgb(226, 229, 255); padding-left: 0.5em;" class="input-block-level" id="node-input-import" rows="5" placeholder="Paste nodes here"></textarea>
</div>
</script>
<script src="jquery/js/jquery-1.9.1.js"></script>
<script src="jquery/js/jquery-1.11.1.min.js"></script>
<script src="bootstrap/js/bootstrap.min.js"></script>
<script src="jquery/js/jquery-ui-1.10.3.custom.min.js"></script>
<script src="jquery/js/jquery.ui.touch-punch.min.js"></script>
@ -272,6 +233,7 @@
<script src="red/nodes.js"></script>
<script src="red/history.js"></script>
<script src="red/validators.js"></script>
<script src="red/ui/menu.js"></script>
<script src="red/ui/keyboard.js"></script>
<script src="red/ui/tabs.js"></script>
<script src="red/ui/view.js"></script>

4
public/jquery/js/jquery-1.11.1.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -15,8 +15,6 @@
**/
var RED = (function() {
$('#btn-keyboard-shortcuts').click(function(){showHelp();});
function hideDropTarget() {
$("#dropTarget").hide();
RED.keyboard.remove(/* ESCAPE */ 27);
@ -201,12 +199,9 @@ var RED = (function() {
});
}
$('#btn-node-status').click(function() {toggleStatus();});
var statusEnabled = false;
function toggleStatus() {
var btnStatus = $("#btn-node-status");
statusEnabled = btnStatus.toggleClass("active").hasClass("active");
function toggleStatus(state) {
statusEnabled = state;
RED.view.status(statusEnabled);
}
@ -229,6 +224,36 @@ var RED = (function() {
}
$(function() {
RED.menu.init({id:"btn-sidemenu",
options: [
{id:"btn-sidebar",icon:"fa fa-columns",label:"Sidebar",toggle:true,onselect:RED.sidebar.toggleSidebar},
null,
{id:"btn-node-status",icon:"fa fa-info",label:"Node Status",toggle:true,onselect:toggleStatus},
null,
{id:"btn-import-menu",icon:"fa fa-sign-in",label:"Import...",options:[
{id:"btn-import-clipboard",icon:"fa fa-clipboard",label:"Clipboard...",onselect:RED.view.showImportNodesDialog},
{id:"btn-import-library",icon:"fa fa-book",label:"Library",options:[]}
]},
{id:"btn-export-menu",icon:"fa fa-sign-out",label:"Export...",disabled:true,options:[
{id:"btn-export-clipboard",icon:"fa fa-clipboard",label:"Clipboard...",disabled:true,onselect:RED.view.showExportNodesDialog},
{id:"btn-export-library",icon:"fa fa-book",label:"Library...",disabled:true,onselect:RED.view.showExportNodesLibraryDialog}
]},
null,
{id:"btn-config-nodes",icon:"fa fa-th-list",label:"Configuration nodes...",onselect:RED.sidebar.config.show},
null,
{id:"btn-workspace-menu",icon:"fa fa-th-large",label:"Workspaces",options:[
{id:"btn-workspace-add",icon:"fa fa-plus",label:"Add"},
{id:"btn-workspace-edit",icon:"fa fa-pencil",label:"Rename"},
{id:"btn-workspace-delete",icon:"fa fa-minus",label:"Delete"},
null
]},
null,
{id:"btn-keyboard-shortcuts",icon:"fa fa-keyboard-o",label:"Keyboard Shortcuts",onselect:showHelp},
{id:"btn-help",icon:"fa fa-question",label:"Help...", href:"http://node-red.github.io/docs"}
]
});
RED.keyboard.add(/* ? */ 191,{shift:true},function(){showHelp();d3.event.preventDefault();});
loadSettings();
RED.comms.connect();

View File

@ -25,6 +25,7 @@ RED.library = (function() {
var li;
var a;
var ul = document.createElement("ul");
ul.id = "btn-import-library-submenu";
ul.className = "dropdown-menu";
if (data.d) {
for (i in data.d) {
@ -61,7 +62,8 @@ RED.library = (function() {
return ul;
};
var menu = buildMenu(data,"");
$("#flow-menu-parent>ul").replaceWith(menu);
//TODO: need an api in RED.menu for this
$("#btn-import-library-submenu").replaceWith(menu);
});
}
loadFlowLibrary();

124
public/red/ui/menu.js Normal file
View File

@ -0,0 +1,124 @@
/**
* Copyright 2014 IBM Corp.
*
* 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.
**/
RED.menu = (function() {
var menuItems = {};
function createMenuItem(opt) {
var item;
if (opt === null) {
item = $('<li class="divider"></li>');
} else {
item = $('<li></li>');
var link = $('<a '+(opt.id?'id="'+opt.id+'" ':'')+'tabindex="-1" href="#">'+
(opt.toggle?'<i class="fa fa-check pull-right"></i>':'')+
(opt.icon?'<i class="'+opt.icon+'"></i> ':'')+
opt.label+
'</a>').appendTo(item);
menuItems[opt.id] = opt;
if (opt.onselect) {
link.click(function() {
if ($(this).parent().hasClass("disabled")) {
return;
}
if (opt.toggle) {
setSelected(opt.id,!isSelected(opt.id));
} else {
opt.onselect.call(opt);
}
})
} else if (opt.href) {
link.attr("target","_blank").attr("href",opt.href);
}
if (opt.options) {
item.addClass("dropdown-submenu pull-left");
var submenu = $('<ul id="'+opt.id+'-submenu" class="dropdown-menu"></ul>').appendTo(item);
for (var i=0;i<opt.options.length;i++) {
createMenuItem(opt.options[i]).appendTo(submenu);
}
}
if (opt.disabled) {
item.addClass("disabled");
}
}
return item;
}
function createMenu(options) {
var button = $("#"+options.id);
var topMenu = $("<ul/>",{class:"dropdown-menu"}).insertAfter(button);
for (var i=0;i<options.options.length;i++) {
var opt = options.options[i];
createMenuItem(opt).appendTo(topMenu);
}
}
function isSelected(id) {
return $("#"+id).hasClass("active");
}
function setSelected(id,state) {
if (isSelected(id) == state) {
return;
}
var opt = menuItems[id];
if (state) {
$("#"+id).addClass("active");
} else {
$("#"+id).removeClass("active");
}
if (opt.onselect) {
opt.onselect.call(opt,state);
}
}
function setDisabled(id,state) {
if (state) {
$("#"+id).parent().addClass("disabled");
} else {
$("#"+id).parent().removeClass("disabled");
}
}
function addItem(id,opt) {
createMenuItem(opt).appendTo("#"+id+"-submenu");
console.log(opt.id);
}
function removeItem(id) {
$("#"+id).parent().remove();
}
return {
init: createMenu,
setSelected: setSelected,
isSelected: isSelected,
setDisabled: setDisabled,
addItem: addItem,
removeItem: removeItem
//TODO: add an api for replacing a submenu - see library.js:loadFlowLibrary
}
})();

View File

@ -34,9 +34,6 @@ RED.sidebar = (function() {
//$('#sidebar').tabs("refresh");
}
$('#btn-sidebar').click(function() {toggleSidebar();});
RED.keyboard.add(/* SPACE */ 32,{ctrl:true},function(){toggleSidebar();d3.event.preventDefault();});
var sidebarSeparator = {};
$("#sidebar-separator").draggable({
axis: "x",
@ -49,14 +46,14 @@ RED.sidebar = (function() {
sidebarSeparator.chartRight = winWidth-$("#workspace").width()-$("#workspace").offset().left-2;
if (!btnSidebar.hasClass("active")) {
if (!RED.menu.isSelected("btn-sidebar")) {
sidebarSeparator.opening = true;
var newChartRight = 15;
$("#sidebar").addClass("closing");
$("#workspace").css("right",newChartRight);
$("#chart-zoom-controls").css("right",newChartRight+20);
$("#sidebar").width(0);
toggleSidebar();
RED.menu.setSelected("btn-sidebar",true);
RED.view.resize();
}
@ -106,7 +103,7 @@ RED.sidebar = (function() {
RED.view.resize();
if (sidebarSeparator.closing) {
$("#sidebar").removeClass("closing");
toggleSidebar();
RED.menu.setSelected("btn-sidebar",false);
if ($("#sidebar").width() < 180) {
$("#sidebar").width(180);
$("#workspace").css("right",208);
@ -118,25 +115,16 @@ RED.sidebar = (function() {
}
});
var btnSidebar = $("#btn-sidebar");
function toggleSidebar() {
//if ($('#sidebar').tabs( "option", "active" ) === false) {
// $('#sidebar').tabs( "option", "active",0);
//}
btnSidebar.toggleClass("active");
if (!btnSidebar.hasClass("active")) {
function toggleSidebar(state) {
if (!state) {
$("#main-container").addClass("sidebar-closed");
} else {
$("#main-container").removeClass("sidebar-closed");
}
}
toggleSidebar();
function showSidebar(id) {
if (!$("#btn-sidebar").hasClass("active")) {
toggleSidebar();
}
RED.menu.setSelected("btn-sidebar",true);
sidebar_tabs.activateTab("tab-"+id);
}
@ -144,10 +132,18 @@ RED.sidebar = (function() {
return sidebar_tabs.contains("tab-"+id);
}
$(function() {
RED.keyboard.add(/* SPACE */ 32,{ctrl:true},function(){RED.menu.setSelected("btn-sidebar",!RED.menu.isSelected("btn-sidebar"));d3.event.preventDefault();});
showSidebar("info");
});
return {
addTab: addTab,
show: showSidebar,
containsTab: containsTab
containsTab: containsTab,
toggleSidebar: toggleSidebar
}
})();

View File

@ -23,13 +23,13 @@ RED.sidebar.config = (function() {
var list = $("<ul>",{class:"tab-config-list"}).appendTo(content);
$("#btn-config-nodes").click(function(){
function show() {
if (!RED.sidebar.containsTab("config")) {
RED.sidebar.addTab("config",content,true);
}
refresh();
RED.sidebar.show("config");
});
}
function refresh() {
list.empty();
@ -78,6 +78,7 @@ RED.sidebar.config = (function() {
});
}
return {
show:show,
refresh:refresh
}
})();

View File

@ -268,29 +268,18 @@ RED.view = (function() {
showRenameWorkspaceDialog(tab.id);
},
onadd: function(tab) {
var menuli = $("<li/>");
var menuA = $("<a/>",{tabindex:"-1",href:"#"+tab.id}).appendTo(menuli);
menuA.html(tab.label);
menuA.on("click",function() {
RED.menu.addItem("btn-workspace-menu",{
id:"btn-workspace-menu-"+tab.id.replace(".","-"),
label:tab.label,
onselect:function() {
workspace_tabs.activateTab(tab.id);
});
$('#workspace-menu-list').append(menuli);
if (workspace_tabs.count() == 1) {
$('#btn-workspace-delete').parent().addClass("disabled");
} else {
$('#btn-workspace-delete').parent().removeClass("disabled");
}
});
RED.menu.setDisabled("btn-workspace-delete",workspace_tabs.count() == 1);
},
onremove: function(tab) {
if (workspace_tabs.count() == 1) {
$('#btn-workspace-delete').parent().addClass("disabled");
} else {
$('#btn-workspace-delete').parent().removeClass("disabled");
}
$('#workspace-menu-list a[href="#'+tab.id+'"]').parent().remove();
RED.menu.setDisabled("btn-workspace-delete",workspace_tabs.count() == 1);
RED.menu.removeItem("btn-workspace-menu-"+tab.id.replace(".","-"));
}
});
@ -309,6 +298,7 @@ RED.view = (function() {
RED.history.push({t:'add',workspaces:[ws],dirty:dirty});
RED.view.dirty(true);
}
$(function() {
$('#btn-workspace-add-tab').on("click",addWorkspace);
$('#btn-workspace-add').on("click",addWorkspace);
$('#btn-workspace-edit').on("click",function() {
@ -317,6 +307,7 @@ RED.view = (function() {
$('#btn-workspace-delete').on("click",function() {
deleteWorkspace(activeWorkspace);
});
});
function deleteWorkspace(id) {
if (workspace_tabs.count() == 1) {
@ -641,13 +632,13 @@ RED.view = (function() {
function updateSelection() {
if (moving_set.length === 0) {
$("#li-menu-export").addClass("disabled");
$("#li-menu-export-clipboard").addClass("disabled");
$("#li-menu-export-library").addClass("disabled");
RED.menu.setDisabled("btn-export-menu",true);
RED.menu.setDisabled("btn-export-clipboard",true);
RED.menu.setDisabled("btn-export-library",true);
} else {
$("#li-menu-export").removeClass("disabled");
$("#li-menu-export-clipboard").removeClass("disabled");
$("#li-menu-export-library").removeClass("disabled");
RED.menu.setDisabled("btn-export-menu",false);
RED.menu.setDisabled("btn-export-clipboard",false);
RED.menu.setDisabled("btn-export-library",false);
}
if (moving_set.length === 0 && selected_link == null) {
RED.keyboard.remove(/* backspace */ 8);
@ -1465,10 +1456,6 @@ RED.view = (function() {
}
}
$('#btn-import').click(function() {showImportNodesDialog();});
$('#btn-export-clipboard').click(function() {showExportNodesDialog();});
$('#btn-export-library').click(function() {showExportNodesLibraryDialog();});
function showExportNodesDialog() {
mouse_mode = RED.state.EXPORT;
var nns = RED.nodes.createExportableNodeSet(moving_set);
@ -1638,6 +1625,11 @@ RED.view = (function() {
RED.nodes.eachNode(function(n) { n.dirty = true;});
//TODO: subscribe/unsubscribe here
redraw();
}
},
//TODO: should these move to an import/export module?
showImportNodesDialog: showImportNodesDialog,
showExportNodesDialog: showExportNodesDialog,
showExportNodesLibraryDialog: showExportNodesLibraryDialog
};
})();

View File

@ -19,6 +19,18 @@ body {
padding-top: 100px;
background: url("pw_maze_white.png");
}
#header {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 40px;
background: #000;
box-sizing: border-box;
padding: 5px 20px;
}
#dropTarget {
position: absolute;
top: 0; bottom: 0;
@ -36,7 +48,9 @@ body {
font-size: 40px;
color: #fff;
}
#dropTarget div i {
font-size: 80px;
}
div.btn-group, a.btn {
-webkit-user-select: none;
-khtml-user-select: none;
@ -45,20 +59,22 @@ div.btn-group, a.btn {
user-select: none;
}
a.brand {
line-height: 16px;
a.logo {
line-height: 30px;
text-decoration: none;
color: #999;
}
a.brand span {
a.logo span {
vertical-align: middle;
font-size: 16px !important;
}
a.brand:hover span.red {
a.logo:hover span.red {
color: rgb(197, 112, 112) !important;
}
a.brand:hover {
a.logo:hover {
color: #bbb !important;
}
a.brand img {
a.logo img {
height: 16px;
}