From 73d8dfe381c365e48516876f63a3b885a6258f75 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 15 Jul 2020 11:26:08 +0100 Subject: [PATCH] Allow sidebar tabs to be reordered The sidebar tab buttons can now be dragged to reorder them. Changes to the order are stored in user preferences. --- .../editor-client/src/js/ui/common/tabs.js | 163 ++++++++++++++++-- .../editor-client/src/js/ui/sidebar.js | 6 +- 2 files changed, 150 insertions(+), 19 deletions(-) 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 f23def3c2..c1346487d 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 @@ -29,7 +29,7 @@ RED.tabs = (function() { var currentTabWidth; var currentActiveTabWidth = 0; var collapsibleMenu; - + var preferredOrder = options.order; var ul = options.element || $("#"+options.id); var wrapper = ul.wrap( "
" ).parent(); var scrollContainer = ul.wrap( "
" ).parent(); @@ -132,11 +132,11 @@ RED.tabs = (function() { activateTab(id); } }; - if (tabs[id].pinned) { - pinnedOptions.push(opt); - } else { + // if (tabs[id].pinned) { + // pinnedOptions.push(opt); + // } else { options.push(opt); - } + // } }); options = pinnedOptions.concat(options); collapsibleMenu = RED.menu.init({options: options}); @@ -363,23 +363,39 @@ RED.tabs = (function() { var tabWidth; if (options.collapsible) { + var availableCount = collapsedButtonsRow.children().length; + var visibleCount = collapsedButtonsRow.children(":visible").length; tabWidth = width - collapsedButtonsRow.width()-10; - if (tabWidth < 198) { - var delta = 198 - tabWidth; + var maxTabWidth = 198; + var minTabWidth = 80; + if (tabWidth <= minTabWidth || (tabWidth < maxTabWidth && visibleCount > 5)) { + // The tab is too small. Hide the next button to make room + // Start at the end of the button row, -1 for the menu button var b = collapsedButtonsRow.find("a:last").prev(); + var index = collapsedButtonsRow.children().length - 2; + // Work backwards to find the first visible button while (b.is(":not(:visible)")) { b = b.prev(); + index--; } - if (!b.hasClass("red-ui-tab-link-button-pinned")) { + // If it isn't a pinned button, hide it to get the room + if (tabWidth <= minTabWidth || visibleCount>6) {//}!b.hasClass("red-ui-tab-link-button-pinned")) { b.hide(); } - tabWidth = width - collapsedButtonsRow.width()-10; + tabWidth = Math.max(minTabWidth,width - collapsedButtonsRow.width()-10); } else { - var space = width - 198 - collapsedButtonsRow.width(); + if (visibleCount !== availableCount) { + if (visibleCount < 6) { + tabWidth = minTabWidth; + } else { + tabWidth = maxTabWidth; + } + } + var space = width - tabWidth - collapsedButtonsRow.width(); if (space > 40) { collapsedButtonsRow.find("a:not(:visible):first").show(); - tabWidth = width - collapsedButtonsRow.width()-10; } + tabWidth = width - collapsedButtonsRow.width()-10; } tabs.css({width:tabWidth}); @@ -469,7 +485,7 @@ RED.tabs = (function() { } } - return { + var tabAPI = { addTab: function(tab,targetIndex) { if (options.onselect) { var selection = ul.find("li.red-ui-tab.selected"); @@ -531,11 +547,92 @@ RED.tabs = (function() { evt.preventDefault(); activateTab(tab.id); }); + pinnedLink.data("tabId",tab.id) if (tab.pinned) { pinnedLink.addClass("red-ui-tab-link-button-pinned"); pinnedTabsCount++; } RED.popover.tooltip($(pinnedLink), tab.name, tab.action); + if (options.onreorder) { + var pinnedLinkIndex; + var pinnedLinks = []; + var startPinnedIndex; + pinnedLink.draggable({ + distance: 10, + axis:"x", + containment: ".red-ui-tab-link-buttons", + start: function(event,ui) { + dragActive = true; + $(".red-ui-tab-link-buttons").width($(".red-ui-tab-link-buttons").width()); + if (dblClickArmed) { dblClickArmed = false; return false } + collapsedButtonsRow.children().each(function(i) { + pinnedLinks[i] = { + el:$(this), + text: $(this).text(), + left: $(this).position().left, + width: $(this).width(), + menu: $(this).hasClass("red-ui-tab-link-button-menu") + }; + if ($(this).is(pinnedLink)) { + pinnedLinkIndex = i; + startPinnedIndex = i; + } + }); + collapsedButtonsRow.children().each(function(i) { + if (i!==pinnedLinkIndex) { + $(this).css({ + position: 'absolute', + left: pinnedLinks[i].left+"px", + width: pinnedLinks[i].width+2, + transition: "left 0.3s" + }); + } + }) + if (!pinnedLink.hasClass('active')) { + pinnedLink.css({'zIndex':1}); + } + }, + drag: function(event,ui) { + ui.position.left += pinnedLinks[pinnedLinkIndex].left; + var tabCenter = ui.position.left + pinnedLinks[pinnedLinkIndex].width/2; + for (var i=0;i pinnedLinks[i].left && tabCenter < pinnedLinks[i].left+pinnedLinks[i].width) { + if (i < pinnedLinkIndex) { + pinnedLinks[i].left += pinnedLinks[pinnedLinkIndex].width+8; + pinnedLinks[pinnedLinkIndex].el.detach().insertBefore(pinnedLinks[i].el); + } else { + pinnedLinks[i].left -= pinnedLinks[pinnedLinkIndex].width+8; + pinnedLinks[pinnedLinkIndex].el.detach().insertAfter(pinnedLinks[i].el); + } + pinnedLinks[i].el.css({left:pinnedLinks[i].left+"px"}); + + pinnedLinks.splice(i, 0, pinnedLinks.splice(pinnedLinkIndex, 1)[0]); + + pinnedLinkIndex = i; + break; + } + } + }, + stop: function(event,ui) { + collapsedButtonsRow.children().css({position:"relative",left:"",transition:""}); + $(".red-ui-tab-link-buttons").width('auto'); + pinnedLink.css({zIndex:""}); + updateTabWidths(); + if (startPinnedIndex !== pinnedLinkIndex) { + if (collapsibleMenu) { + collapsibleMenu.remove(); + collapsibleMenu = null; + } + var newOrder = $.makeArray(collapsedButtonsRow.children().map(function() { return $(this).data('tabId');})); + tabAPI.order(newOrder); + options.onreorder(newOrder); + } + } + }); + } } link.on("mouseup",onTabClick); @@ -565,7 +662,7 @@ RED.tabs = (function() { if (ul.find("li.red-ui-tab").length == 1) { activateTab(link); } - if (options.onreorder) { + if (options.onreorder && !options.collapsible) { var originalTabOrder; var tabDragIndex; var tabElements = []; @@ -652,6 +749,9 @@ RED.tabs = (function() { collapsibleMenu.remove(); collapsibleMenu = null; } + if (preferredOrder) { + tabAPI.order(preferredOrder); + } }, removeTab: removeTab, activateTab: activateTab, @@ -673,10 +773,8 @@ RED.tabs = (function() { }, selection: getSelection, order: function(order) { + preferredOrder = order; var existingTabOrder = $.makeArray(ul.children().map(function() { return $(this).data('tabId');})); - if (existingTabOrder.length !== order.length) { - return - } var i; var match = true; for (i=0;i