').appendTo(options.target);
+
+ // Don't use the `hide` class on this container, as the show reverts it to block rather
+ // than the expected flex. So hide via jQuery as it'll track the show state internally.
+ options.target.find('#red-ui-main-container').hide()
+
$('').appendTo(options.target);
$('').appendTo(options.target);
$('').appendTo(options.target);
+ $('').appendTo(options.target);
loader.init().appendTo("#red-ui-main-container");
loader.start("...",0);
diff --git a/packages/node_modules/@node-red/editor-client/src/js/settings.js b/packages/node_modules/@node-red/editor-client/src/js/settings.js
index 85c930bfbb..f7048df710 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/settings.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/settings.js
@@ -29,7 +29,16 @@ RED.settings = (function () {
}
};
- var set = function (key, value) {
+ /**
+ * Set a setting in the user settings within the runtime.
+ * Calls to this function are debounced to avoid excessive calls to the runtime when multiple settings are changed in quick succession.
+ * The flush parameter can be set to true to bypass the debounce and immediately save the settings to the runtime.
+ * @param {string} key
+ * @param {*} value
+ * @param {boolean} flush
+ * @returns
+ */
+ var set = function (key, value, flush) {
if (!hasLocalStorage()) {
return;
}
@@ -37,7 +46,7 @@ RED.settings = (function () {
localStorage.setItem(key+this.authTokensSuffix, JSON.stringify(value));
} else {
RED.utils.setMessageProperty(userSettings,key,value);
- saveUserSettings();
+ saveUserSettings(flush);
}
};
@@ -188,13 +197,12 @@ RED.settings = (function () {
});
}
- function saveUserSettings() {
+ function saveUserSettings(flush) {
if (RED.user.hasPermission("settings.write")) {
if (pendingSave) {
clearTimeout(pendingSave);
}
- pendingSave = setTimeout(function() {
- pendingSave = null;
+ const save = () => {
$.ajax({
method: 'POST',
contentType: 'application/json',
@@ -206,7 +214,15 @@ RED.settings = (function () {
console.log("Unexpected error saving user settings:",jqXHR.status,textStatus);
}
});
- },300);
+ }
+ if (flush) {
+ save()
+ } else {
+ pendingSave = setTimeout(function() {
+ pendingSave = null;
+ save();
+ }, 300);
+ }
}
}
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/actionList.js b/packages/node_modules/@node-red/editor-client/src/js/ui/actionList.js
index d47a20f5d2..739d39d19a 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/actionList.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/actionList.js
@@ -43,7 +43,7 @@ RED.actionList = (function() {
}
function createDialog() {
- dialog = $("
').insertAfter(children[0]);
+ if (options.invisibleSeparator) {
+ if (!vertical) {
+ throw new Error("invisibleSeparator option is only valid for vertical panels");
+ }
+ separator.addClass("red-ui-panels-separator-invisible");
+ $('').appendTo(separator)
+ }
var startPosition;
var panelSizes = [];
var modifiedSizes = false;
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js
index 7ae05e770f..16788bc74b 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js
@@ -30,6 +30,7 @@ RED.tabs = (function() {
var currentActiveTabWidth = 0;
var collapsibleMenu;
var mousedownTab;
+ var mouseclickTab;
var preferredOrder = options.order;
var ul = options.element || $("#"+options.id);
var wrapper = ul.wrap( "
" ).parent();
@@ -39,6 +40,34 @@ RED.tabs = (function() {
wrapper.addClass("red-ui-tabs-vertical");
}
+ var scrollLeft;
+ var scrollRight;
+
+ if (options.scrollable) {
+ wrapper.addClass("red-ui-tabs-scrollable");
+ scrollContainer.addClass("red-ui-tabs-scroll-container");
+ scrollContainer.on("scroll",function(evt) {
+ // Generated by trackpads - not mousewheel
+ updateScroll(evt);
+ });
+ scrollContainer.on("wheel", function(evt) {
+ if (evt.originalEvent.deltaX === 0) {
+ // Prevent the scroll event from firing
+ evt.preventDefault();
+
+ // Assume this is wheel event which might not trigger
+ // the scroll event, so do things manually
+ var sl = scrollContainer.scrollLeft();
+ sl += evt.originalEvent.deltaY;
+ scrollContainer.scrollLeft(sl);
+ }
+ })
+ scrollLeft = $('
',{style:"height:100%"}).appendTo(targetSidebar.sections[targetSection].content)
options.wrapper.append(options.content);
options.wrapper.hide();
@@ -55,11 +168,12 @@ RED.sidebar = (function() {
}
if (options.toolbar) {
- $("#red-ui-sidebar-footer").append(options.toolbar);
+ targetSidebar.sections[targetSection].footer.append(options.toolbar);
$(options.toolbar).hide();
}
var id = options.id;
+ // console.log('menu', options.id, options.name)
RED.menu.addItem("menu-item-view-menu",{
id:"menu-item-view-menu-"+options.id,
label:options.name,
@@ -72,208 +186,577 @@ RED.sidebar = (function() {
options.iconClass = options.iconClass || "fa fa-square-o"
knownTabs[options.id] = options;
+ options.tabButton = $('')
+ // Insert the tab button at the correct index
+ if (targetTabButtonIndex === -1) {
+ // Append to end
+ targetSidebar.tabBars[targetSection].addButton(options.tabButton)
+ } else {
+ // Insert before the item at targetTabButtonIndex
+ targetSidebar.tabBars[targetSection].addButton(options.tabButton, targetTabButtonIndex)
+ }
+ options.tabButton.attr('data-tab-id', options.id)
- if (options.visible !== false) {
- sidebar_tabs.addTab(knownTabs[options.id]);
+ options.tabButtonTooltip = RED.popover.tooltip(options.tabButton, options.name, options.action);
+ if (options.icon) {
+ $('',{class: 'red-ui-sidebar-tab-icon', style:"mask-image: url("+options.icon+"); -webkit-mask-image: url("+options.icon+");"}).appendTo(options.tabButton);
+ } else if (options.iconClass) {
+ $('',{class:options.iconClass}).appendTo(options.tabButton);
}
+ options.tabButton.on('mouseup', function(evt) {
+ if (draggingTabButton) {
+ draggingTabButton = false
+ return
+ }
+ const targetSidebar = options.target === 'secondary' ? sidebars.secondary : sidebars.primary;
+ if (targetSidebar.tabBars[options.targetSection].active === options.id && RED.menu.isSelected(targetSidebar.menuToggle)) {
+ if (!targetSidebar.sections[options.targetSection].hidden) {
+ const otherSectionHidden = targetSidebar.sections[options.targetSection === 'top' ? 'bottom' : 'top'].hidden
+ if (otherSectionHidden) {
+ // Both sections are going to be hidden, so hide the sidebar first.
+ // We do this *before* hiding the last section so that we remember which the 'last' section was and it can be
+ // restored when the sidebar is shown again.
+ RED.menu.setSelected(targetSidebar.menuToggle, false);
+ } else {
+ // Hiding just one section, clear its active setting
+ targetSidebar.tabBars[targetSection].active = null
+ }
+ targetSidebar.hideSection(options.targetSection)
+ } else {
+ targetSidebar.showSection(options.targetSection)
+ }
+ exportSidebarState()
+ } else {
+ RED.sidebar.show(options.id)
+ }
+ })
}
function removeTab(id) {
- sidebar_tabs.removeTab(id);
- $(knownTabs[id].wrapper).remove();
- if (knownTabs[id].footer) {
- knownTabs[id].footer.remove();
+ if (knownTabs[id]) {
+ const targetSidebar = knownTabs[id].target === 'secondary' ? sidebars.secondary : sidebars.primary;
+ $(knownTabs[id].wrapper).remove();
+ if (knownTabs[id].footer) {
+ knownTabs[id].footer.remove();
+ }
+ targetSidebar.tabBar.find('button[data-tab-id="'+id+'"]').remove()
+ RED.menu.removeItem("menu-item-view-menu-"+id);
+ if (knownTabs[id].onremove) {
+ knownTabs[id].onremove.call(knownTabs[id]);
+ }
+ delete knownTabs[id];
+ const firstTab = targetSidebar.tabBar.find('button').first().attr('data-tab-id');
+ if (firstTab) {
+ RED.sidebar.show(firstTab);
+ }
}
- delete knownTabs[id];
- RED.menu.removeItem("menu-item-view-menu-"+id);
}
- var sidebarSeparator = {};
- sidebarSeparator.dragging = false;
-
- function setupSidebarSeparator() {
- $("#red-ui-sidebar-separator").draggable({
- axis: "x",
- start:function(event,ui) {
- sidebarSeparator.closing = false;
- sidebarSeparator.opening = false;
- var winWidth = $("#red-ui-editor").width();
- sidebarSeparator.start = ui.position.left;
- sidebarSeparator.chartWidth = $("#red-ui-workspace").width();
- sidebarSeparator.chartRight = winWidth-$("#red-ui-workspace").width()-$("#red-ui-workspace").offset().left-2;
- sidebarSeparator.dragging = true;
-
- if (!RED.menu.isSelected("menu-item-sidebar")) {
- sidebarSeparator.opening = true;
- var newChartRight = 7;
- $("#red-ui-sidebar").addClass("closing");
- $("#red-ui-workspace").css("right",newChartRight);
- $("#red-ui-editor-stack").css("right",newChartRight+1);
- $("#red-ui-sidebar").width(0);
- RED.menu.setSelected("menu-item-sidebar",true);
- RED.events.emit("sidebar:resize");
- }
- sidebarSeparator.width = $("#red-ui-sidebar").width();
- },
- drag: function(event,ui) {
- var d = ui.position.left-sidebarSeparator.start;
- var newSidebarWidth = sidebarSeparator.width-d;
- if (sidebarSeparator.opening) {
- newSidebarWidth -= 3;
- }
+ function moveTab(id, srcSidebar, srcPosition, targetSidebar, targetPosition) {
+ const options = knownTabs[id];
+ options.target = targetSidebar.id;
+ options.targetSection = targetPosition;
- if (newSidebarWidth > 150) {
- if (sidebarSeparator.chartWidth+d < 200) {
- ui.position.left = 200+sidebarSeparator.start-sidebarSeparator.chartWidth;
- d = ui.position.left-sidebarSeparator.start;
- newSidebarWidth = sidebarSeparator.width-d;
- }
- }
+ $(options.wrapper).appendTo(targetSidebar.sections[targetPosition].content);
+ if (options.toolbar) {
+ targetSidebar.sections[targetPosition].footer.append(options.toolbar);
+ }
+ // Reset the tooltip so its left/right direction is recalculated
+ options.tabButtonTooltip.delete()
+ options.tabButtonTooltip = RED.popover.tooltip(options.tabButton, options.name, options.action);
- if (newSidebarWidth < 150) {
- if (!sidebarSeparator.closing) {
- $("#red-ui-sidebar").addClass("closing");
- sidebarSeparator.closing = true;
- }
- if (!sidebarSeparator.opening) {
- newSidebarWidth = 150;
- ui.position.left = sidebarSeparator.width-(150 - sidebarSeparator.start);
- d = ui.position.left-sidebarSeparator.start;
- }
- } else if (newSidebarWidth > 150 && (sidebarSeparator.closing || sidebarSeparator.opening)) {
- sidebarSeparator.closing = false;
- $("#red-ui-sidebar").removeClass("closing");
- }
+ if (targetSidebar.sections[targetPosition].content.children().length === 1) {
+ RED.sidebar.show(options.id)
+ }
+ if (srcSidebar.sections[srcPosition].content.children().length === 0) {
+ // src has been emptied
+ srcSidebar.hideSection(srcPosition)
+ srcSidebar.tabBars[srcPosition].container.addClass('red-ui-sidebar-tab-bar-empty')
+ }
+ if (targetSidebar.sections[targetPosition].content.children().length > 0) {
+ targetSidebar.tabBars[targetPosition].container.removeClass('red-ui-sidebar-tab-bar-empty')
+ }
+
+ if (targetSidebar.sections[targetPosition].hidden) {
+ targetSidebar.showSection(targetPosition)
+ }
+ }
- var newChartRight = sidebarSeparator.chartRight-d;
- $("#red-ui-workspace").css("right",newChartRight);
- $("#red-ui-editor-stack").css("right",newChartRight+1);
- $("#red-ui-sidebar").width(newSidebarWidth);
+ let draggingTabButton = false
- sidebar_tabs.resize();
- RED.events.emit("sidebar:resize");
- },
- stop:function(event,ui) {
- sidebarSeparator.dragging = false;
- if (sidebarSeparator.closing) {
- $("#red-ui-sidebar").removeClass("closing");
- RED.menu.setSelected("menu-item-sidebar",false);
- if ($("#red-ui-sidebar").width() < 180) {
- $("#red-ui-sidebar").width(180);
- $("#red-ui-workspace").css("right",187);
- $("#red-ui-editor-stack").css("right",188);
+ function setupSidebarTabs(sidebar) {
+ const tabBar = $('').addClass('red-ui-sidebar-' + sidebar.direction);
+ if (sidebar.direction === 'right') {
+ tabBar.appendTo("#red-ui-workspace-footer");
+ } else if (sidebar.direction === 'left') {
+ tabBar.prependTo("#red-ui-workspace-footer");
+ }
+
+ // TODO: consider an explicit toggle button for the sidebars...
+ // const toggleSidebarButton = $('')
+ // toggleSidebarButton.on('click', function() {
+ // RED.menu.toggleSelected(sidebar.menuToggle);
+ // })
+ sidebar.tabBars = {
+ top: setupTabSection(sidebar, tabBar, 'top'),
+ bottom: setupTabSection(sidebar, tabBar, 'bottom')
+ }
+ // if (sidebar.direction === 'right') {
+ // toggleSidebarButton.appendTo(tabBar);
+ // } else if (sidebar.direction === 'left') {
+ // toggleSidebarButton.prependTo(tabBar);
+ // }
+
+
+
+ sidebar.tabBar = tabBar // sidebar.tabBars.top.container;
+ sidebar.hideSection = function (position) {
+ // Track the height of the top section as that is the one that will determine the layout - but only if the other section is visible.
+ const otherPosition = position === 'top' ? 'bottom' : 'top'
+ if (!sidebar.sections[otherPosition].hidden) {
+ sidebar.sections.top.height = sidebar.sections.top.container.height() || 300
+ }
+ sidebar.sections[position].container.hide()
+ sidebar.sections[position].hidden = true
+
+ sidebar.sections[otherPosition].container.css('flex-grow', '1')
+ if (otherPosition === 'bottom') {
+ sidebar.sections[otherPosition].container.css('margin-top', sidebar.direction === 'left' ? '0' : '4px')
+ }
+ sidebar.tabBars[position].clearSelected()
+
+ sidebar.resizeSidebar()
+ }
+ sidebar.showSection = function (position) {
+ sidebar.sections[position].container.show()
+ sidebar.sections[position].hidden = false
+ const otherPosition = position === 'top' ? 'bottom' : 'top'
+ sidebar.sections[otherPosition].container.css('flex-grow', '0')
+ sidebar.sections.top.container.css('height', sidebar.sections.top.height + 'px')
+ sidebar.sections.bottom.container.css('height', '100%')
+ if (otherPosition === 'bottom') {
+ sidebar.sections[otherPosition].container.css('margin-top', '')
+ }
+ sidebar.tabBars[position].container.find('button[data-tab-id="'+sidebar.tabBars[position].active+'"]').addClass('selected')
+ sidebar.resizeSidebar()
+ }
+ sidebar.hideSection('top')
+ sidebar.hideSection('bottom')
+ }
+
+ function setupTabSection(sidebar, tabBar, position) {
+ const tabBarButtonsContainer = $('').appendTo(tabBar);
+ const tabOverflowButton = $('').appendTo(tabBarButtonsContainer);
+ tabOverflowButton.hide()
+ tabOverflowButton.on('click', function(evt) {
+ try {
+ const menuOptions = []
+ tabBarButtonsContainer.find('button:not(.red-ui-sidebar-tab-bar-overflow-button)').each(function () {
+ if ($(this).is(':visible')) {
+ return
+ }
+ const tabId = $(this).attr('data-tab-id')
+ const tabOptions = knownTabs[tabId]
+ menuOptions.push({
+ label: tabOptions.name,
+ onselect: function() {
+ RED.sidebar.show(tabId)
}
+ })
+ })
+ if (menuOptions.length === 0) {
+ return
+ }
+ const menu = RED.menu.init({ options: menuOptions });
+ menu.attr("id",sidebar.container.attr('id')+"-menu");
+ menu.css({
+ position: "absolute"
+ })
+ menu.appendTo("body");
+ var elementPos = tabOverflowButton.offset();
+ menu.css({
+ top: (elementPos.top+tabOverflowButton.height()- menu.height() - 10)+"px",
+ left: sidebar.direction === 'left' ? ((elementPos.left + tabOverflowButton.width() + 3)+"px") : ((elementPos.left - menu.width() - 3)+"px")
+ })
+ $(".red-ui-menu.red-ui-menu-dropdown").hide();
+ setTimeout(() => {
+ $(document).on("click.red-ui-sidebar-tabmenu", function(evt) {
+ $(document).off("click.red-ui-sidebar-tabmenu");
+ menu.remove();
+ });
+ }, 0)
+ menu.show();
+ } catch (err) {
+ console.log(err)
+ }
+ })
+ tabBarButtonsContainer.data('sidebar', sidebar.id)
+ tabBarButtonsContainer.data('sidebar-position', position)
+ tabBarButtonsContainer.sortable({
+ axis: 'x',
+ distance: 10,
+ cancel: false,
+ items: "button:not(.red-ui-sidebar-tab-bar-overflow-button)",
+ placeholder: "red-ui-sidebar-tab-bar-button-placeholder",
+ connectWith: ".red-ui-sidebar-tab-bar-buttons",
+ start: function(event, ui) {
+ const tabId = ui.item.attr('data-tab-id');
+ const options = knownTabs[tabId];
+ options.tabButtonTooltip.delete()
+ draggingTabButton = true
+ tabBar.css('z-index','inherit')
+ $(".red-ui-sidebar-tab-bar").addClass("red-ui-sidebar-dragging-tab");
+ },
+ stop: function(event, ui) {
+ // Restore the tooltip
+ const tabId = ui.item.attr('data-tab-id');
+ const options = knownTabs[tabId];
+ options.tabButtonTooltip.delete()
+ options.tabButtonTooltip = RED.popover.tooltip(options.tabButton, options.name, options.action);
+ // Save the sidebar state
+ exportSidebarState()
+ tabBar.css('z-index','')
+ $(".red-ui-sidebar-tab-bar").removeClass("red-ui-sidebar-dragging-tab");
+ },
+ receive: function(event, ui) {
+ // Tab has been moved from one sidebar to another
+ const src = sidebars[ui.sender.data('sidebar')]
+ const dest = sidebars[$(this).data('sidebar')]
+ const srcPosition = ui.sender.data('sidebar-position')
+ const destPosition = $(this).data('sidebar-position')
+ const tabId = ui.item.attr('data-tab-id');
+ moveTab(tabId, src, srcPosition, dest, destPosition);
+ if (ui.item.hasClass('selected')) {
+ const firstTab = src.tabBars[srcPosition].container.find('button').first().attr('data-tab-id');
+ if (firstTab) {
+ RED.sidebar.show(firstTab);
}
- $("#red-ui-sidebar-separator").css("left","auto");
- $("#red-ui-sidebar-separator").css("right",($("#red-ui-sidebar").width()+2)+"px");
- RED.events.emit("sidebar:resize");
}
- });
-
- var sidebarControls = $('
').appendTo($("#red-ui-sidebar-separator"));
- sidebarControls.on("click", function() {
- sidebarControls.hide();
- RED.menu.toggleSelected("menu-item-sidebar");
+ RED.sidebar.show(tabId)
+ }
})
- $("#red-ui-sidebar-separator").on("mouseenter", function() {
- if (!sidebarSeparator.dragging) {
- if (RED.menu.isSelected("menu-item-sidebar")) {
- sidebarControls.find("i").addClass("fa-chevron-right").removeClass("fa-chevron-left");
+
+ return {
+ container: tabBarButtonsContainer,
+ addButton: function(button, position) {
+ if (position === undefined || position >= tabBarButtonsContainer.children().length) {
+ button.insertBefore(tabOverflowButton);
} else {
- sidebarControls.find("i").removeClass("fa-chevron-right").addClass("fa-chevron-left");
+ button.insertBefore(tabBarButtonsContainer.children().eq(position));
}
- sidebarControls.toggle("slide", { direction: "right" }, 200);
+ },
+ clearSelected: function() {
+ tabBarButtonsContainer.children('button').removeClass('selected')
}
- })
- $("#red-ui-sidebar-separator").on("mouseleave", function() {
- if (!sidebarSeparator.dragging) {
- sidebarControls.stop(false,true);
- sidebarControls.hide();
+ }
+ }
+ function setupSidebarSeparator(sidebar) {
+ const separator = $('');
+ separator.attr('id', sidebar.container.attr('id') + '-separator')
+ $('').appendTo(separator);
+ $('').appendTo(separator);
+ let scaleFactor = 1;
+ if (sidebar.direction === 'right') {
+ separator.insertBefore(sidebar.container);
+ } else if (sidebar.direction === 'left') {
+ scaleFactor = -1;
+ separator.insertAfter(sidebar.container);
+ }
+ // Track sidebar state whilst dragging
+ const sidebarSeparator = {}
+ separator.draggable({
+ axis: "x",
+ start:function(event,ui) {
+ sidebarSeparator.closing = false;
+ sidebarSeparator.opening = false;
+ // var winWidth = $("#red-ui-editor").width();
+ sidebarSeparator.start = ui.position.left;
+ sidebarSeparator.width = sidebar.container.width();
+ sidebarSeparator.chartWidth = $("#red-ui-workspace").width();
+ sidebarSeparator.dragging = true;
+
+ if (!RED.menu.isSelected(sidebar.menuToggle)) {
+ sidebarSeparator.opening = true;
+ sidebar.container.width(0);
+ RED.menu.setSelected(sidebar.menuToggle,true);
+ RED.events.emit("sidebar:resize");
+ }
+ sidebarSeparator.width = sidebar.container.width();
+ },
+ drag: function(event,ui) {
+ var d = scaleFactor * (ui.position.left-sidebarSeparator.start);
+
+ var newSidebarWidth = sidebarSeparator.width - d;
+ if (newSidebarWidth > sidebar.maximumWidth) {
+ newSidebarWidth = sidebar.maximumWidth;
+ d = sidebarSeparator.width - sidebar.maximumWidth;
+ ui.position.left = sidebarSeparator.start + scaleFactor * d;
+ }
+
+ if (newSidebarWidth > sidebar.minimumWidth) {
+ if (sidebarSeparator.chartWidth + d < 200) {
+ // Chart is now too small, but we have room to resize the sidebar
+ d += (200 - (sidebarSeparator.chartWidth + d));
+ newSidebarWidth = sidebarSeparator.width - d;
+ ui.position.left = sidebarSeparator.start + scaleFactor * d;
+ }
+ } else if (newSidebarWidth < sidebar.minimumWidth) {
+ if (newSidebarWidth > 100) {
+ newSidebarWidth = sidebar.minimumWidth
+ sidebarSeparator.closing = false
+ } else {
+ newSidebarWidth = 0
+ sidebarSeparator.closing = true
+ }
+ } else {
+ sidebarSeparator.closing = false
+ }
+ sidebar.container.width(newSidebarWidth);
+ ui.position.left -= scaleFactor * d
+ sidebar.tabBar.css('min-width', sidebar.container.width() - 5)
+ RED.events.emit("sidebar:resize");
+ },
+ stop:function(event,ui) {
+ sidebarSeparator.dragging = false;
+ if (sidebarSeparator.closing) {
+ sidebar.container.removeClass("closing");
+ if (sidebar.menuToggle) {
+ RED.menu.setSelected(sidebar.menuToggle,false);
+ }
+ sidebar.sections.top.container.hide()
+ sidebar.sections.bottom.container.hide()
+ sidebar.separator.hide()
+ } else {
+ sidebar.width = sidebar.container.width();
+ }
+ sidebar.tabBar.css('min-width', sidebar.container.width() - 5)
+ RED.events.emit("sidebar:resize");
}
});
+ return separator
}
- function toggleSidebar(state) {
+ function toggleSidebar(sidebar, state) {
if (!state) {
- $("#red-ui-main-container").addClass("red-ui-sidebar-closed");
+ // sidebar.container.hide()
+ sidebar.separator.hide()
+ // Remember which sections were hidden (or not) before we hide the sidebar - so we can restore that state when we show the sidebar again
+ sidebar.sections.top.wasHidden = sidebar.sections.top.hidden
+ sidebar.sections.bottom.wasHidden = sidebar.sections.bottom.hidden
+ sidebar.hideSection('top')
+ sidebar.hideSection('bottom')
+ sidebar.container.width(0)
+ if (sidebarsInitialised) {
+ exportSidebarState()
+ }
} else {
- $("#red-ui-main-container").removeClass("red-ui-sidebar-closed");
- sidebar_tabs.resize();
+ // sidebar.container.show()
+ if (!sidebar.sections.top.hidden) {
+
+ sidebar.sections.top.container.show()
+ }
+ if (!sidebar.sections.bottom.hidden) {
+ sidebar.sections.bottom.container.show()
+ }
+ if (sidebar.sections.top.hidden && sidebar.sections.bottom.hidden) {
+ const topHasContent = sidebar.sections.top.content.children().length > 0
+ const bottomHasContent = sidebar.sections.bottom.content.children().length > 0
+ if (!topHasContent && !bottomHasContent) {
+ // Nothing to show - keep the sidebar hidden
+ return
+ }
+ if (sidebar.tabBars.top.active) {
+ showSidebar(sidebar.tabBars.top.active)
+ }
+ if (sidebar.tabBars.bottom.active) {
+ showSidebar(sidebar.tabBars.bottom.active)
+ }
+ }
+ sidebar.container.width(sidebar.width || sidebar.defaultWidth)
+ sidebar.tabBar.css('min-width', sidebar.container.width() - 5)
+ sidebar.separator.show()
+ if (sidebar.tabBars.top.active && !sidebar.sections.top.hidden) {
+ sidebar.tabBars.top.container.find('button[data-tab-id="'+sidebar.tabBars.top.active+'"]').addClass('selected')
+ }
+ if (sidebar.tabBars.bottom.active && !sidebar.sections.bottom.hidden) {
+ sidebar.tabBars.bottom.container.find('button[data-tab-id="'+sidebar.tabBars.bottom.active+'"]').addClass('selected')
+ }
}
RED.events.emit("sidebar:resize");
}
function showSidebar(id, skipShowSidebar) {
if (id === ":first") {
- id = lastSessionSelectedTab || RED.settings.get("editor.sidebar.order",["info", "help", "version-control", "debug"])[0]
+ sidebarsInitialised = true
+ // Show the last selected tab for each sidebar
+ Object.keys(sidebars).forEach(function(sidebarKey) {
+ const sidebar = sidebars[sidebarKey];
+ ['top','bottom'].forEach(function(position) {
+ if (lastSessionSelectedTabs[sidebarKey]?.[position]?.hidden !== true) {
+ let lastTabId = lastSessionSelectedTabs[sidebarKey]?.[position]?.active;
+ if (lastTabId) {
+ showSidebar(lastTabId)
+ }
+ }
+ })
+ })
+ return
}
if (id) {
- if (!containsTab(id) && knownTabs[id]) {
- sidebar_tabs.addTab(knownTabs[id]);
- }
- sidebar_tabs.activateTab(id);
- if (!skipShowSidebar && !RED.menu.isSelected("menu-item-sidebar")) {
- RED.menu.setSelected("menu-item-sidebar",true);
+ const tabOptions = knownTabs[id];
+ if (tabOptions) {
+ const targetSidebar = tabOptions.target === 'secondary' ? sidebars.secondary : sidebars.primary;
+ const targetSection = tabOptions.targetSection || 'top'
+ targetSidebar.sections[targetSection].content.children().hide();
+ targetSidebar.sections[targetSection].footer.children().hide();
+ if (tabOptions.onchange) {
+ tabOptions.onchange.call(tabOptions);
+ }
+ $(tabOptions.wrapper).show();
+ if (tabOptions.toolbar) {
+ $(tabOptions.toolbar).show();
+ targetSidebar.sections[targetSection].footer.show();
+ } else {
+ targetSidebar.sections[targetSection].footer.hide();
+ }
+ targetSidebar.tabBars[targetSection].active = id
+ if (!skipShowSidebar && targetSidebar.sections[targetSection].hidden) {
+ targetSidebar.showSection(targetSection)
+ }
+ if (!skipShowSidebar && !RED.menu.isSelected(targetSidebar.menuToggle)) {
+ RED.menu.setSelected(targetSidebar.menuToggle,true);
+ }
+ if (!targetSidebar.sections[targetSection].hidden) {
+ targetSidebar.tabBars[targetSection].clearSelected()
+ targetSidebar.tabBars[targetSection].container.find('button[data-tab-id="'+id+'"]').addClass('selected')
+ }
+ exportSidebarState()
}
}
}
function containsTab(id) {
- return sidebar_tabs.contains(id);
+ return sidebars.primary.tabs.contains(id);
}
- function init () {
- setupSidebarSeparator();
- sidebar_tabs = RED.tabs.create({
- element: $('
').appendTo("#red-ui-sidebar"),
- onchange:function(tab) {
- $("#red-ui-sidebar-content").children().hide();
- $("#red-ui-sidebar-footer").children().hide();
- if (tab.onchange) {
- tab.onchange.call(tab);
- }
- $(tab.wrapper).show();
- if (tab.toolbar) {
- $(tab.toolbar).show();
- }
- RED.settings.setLocal("last-sidebar-tab", tab.id)
+ function setupSidebar(sidebar) {
+ // Get the appropriate height for the sidebar - as the sidebar will be hidden at this point in time, we need to use
+ // the main-container height as a decent proxy
+ const sidebarHeight = $("#red-ui-main-container").height();
+ sidebar.container.addClass("red-ui-sidebar").addClass('red-ui-sidebar-' + sidebar.direction);
+ sidebar.container.width(sidebar.defaultWidth);
+ if (sidebar.direction === 'right') {
+ $('
Désormais vous recevrez une notification lorsqu'une nouvelle version de Node-RED ou une nouvelle version relative à un des noeuds que vous avez installés est disponible
The core nodes have received lots of minor fixes, documentation updates and
+ small enhancements. Check the full changelog in the Help sidebar for a full list.
+
+
Support for node: prefixed modules in the Function node
+
The ability to set a global timeout for Function nodes via the runtime settings
+
Better display of error objects in the Debug sidebar
Les noeuds principaux ont bénéficié de nombreux correctifs mineurs, de mises à jour de documentation et d'améliorations mineures.
+ Consultez le journal complet des modifications dans la barre latérale d'aide pour une liste complète.
+
+
Prise en charge des modules préfixés node: dans le noeud Fonction.
+
Possibilité de définir un délai d'expiration global pour les noeuds Fonction via les paramètres d'exécution.
+
Meilleur affichage des objets d'erreur dans la barre latérale de débogage.
Let's take a moment to discover the new features in this release.
",
- "ja": "
本リリースの新機能を見つけてみましょう。
",
- "fr": "
Prenons un moment pour découvrir les nouvelles fonctionnalités de cette version.
"
+ "en-US": `
+
As a beta release, this is a step towards the final version of Node-RED 5.0.
+
This release has focused on giving the flows more space; moving the editor tabs into the header and the sidebar buttons along the bottom of the screen.
Désormais vous recevrez une notification lorsqu'une nouvelle version de Node-RED ou une nouvelle version relative à un des noeuds que vous avez installés est disponible
Les flux exportés depuis Node-RED 4.1 incluent désormais des informations sur les modules supplémentaires à installer.
-
Lors de l'importation d'un flux contenant ces informations, l'éditeur vous indiquera les modules manquants et vous aidera à les installer.
+ "en-US": `
+
We're nearing the end-point for the UX changes for the editor, so we can start to address other items in the plan, including the long-awaited
+ built-in dark theme.
+
As always, we welcome your feedback on the new design and features - please join us on the forum or GitHub to let us know your thoughts!
The core nodes have received lots of minor fixes, documentation updates and
- small enhancements. Check the full changelog in the Help sidebar for a full list.
-
-
Support for node: prefixed modules in the Function node
-
The ability to set a global timeout for Function nodes via the runtime settings
-
Better display of error objects in the Debug sidebar
Les noeuds principaux ont bénéficié de nombreux correctifs mineurs, de mises à jour de documentation et d'améliorations mineures.
- Consultez le journal complet des modifications dans la barre latérale d'aide pour une liste complète.
-
-
Prise en charge des modules préfixés node: dans le noeud Fonction.
-
Possibilité de définir un délai d'expiration global pour les noeuds Fonction via les paramètres d'exécution.
-
Meilleur affichage des objets d'erreur dans la barre latérale de débogage.