mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Add context menu to tab bar
This commit is contained in:
		@@ -57,11 +57,11 @@
 | 
			
		||||
        "addFlowToRight": "Add flow to the right",
 | 
			
		||||
        "hideFlow": "Hide flow",
 | 
			
		||||
        "hideOtherFlows": "Hide other flows",
 | 
			
		||||
        "showAllFlows": "Show all flows",
 | 
			
		||||
        "showAllFlows": "Show all flows (__count__ hidden)",
 | 
			
		||||
        "hideAllFlows": "Hide all flows",
 | 
			
		||||
        "hiddenFlows": "List __count__ hidden flow",
 | 
			
		||||
        "hiddenFlows_plural": "List __count__ hidden flows",
 | 
			
		||||
        "showLastHiddenFlow": "Show last hidden flow",
 | 
			
		||||
        "showLastHiddenFlow": "Reopen hidden flow",
 | 
			
		||||
        "listFlows": "List flows",
 | 
			
		||||
        "listSubflows": "List subflows",
 | 
			
		||||
        "status": "Status",
 | 
			
		||||
 
 | 
			
		||||
@@ -423,11 +423,10 @@ RED.clipboard = (function() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function showImportNodes(mode) {
 | 
			
		||||
    function showImportNodes(library = 'clipboard') {
 | 
			
		||||
        if (disabled) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        mode = mode || "clipboard";
 | 
			
		||||
 | 
			
		||||
        dialogContainer.empty();
 | 
			
		||||
        dialogContainer.append($(importNodesDialog));
 | 
			
		||||
@@ -533,8 +532,8 @@ RED.clipboard = (function() {
 | 
			
		||||
            $("#red-ui-clipboard-dialog-import-file-upload").trigger("click");
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        tabs.activateTab("red-ui-clipboard-dialog-import-tab-"+mode);
 | 
			
		||||
        if (mode === 'clipboard') {
 | 
			
		||||
        tabs.activateTab("red-ui-clipboard-dialog-import-tab-"+library);
 | 
			
		||||
        if (library === 'clipboard') {
 | 
			
		||||
            setTimeout(function() {
 | 
			
		||||
                $("#red-ui-clipboard-dialog-import-text").trigger("focus");
 | 
			
		||||
            },100)
 | 
			
		||||
@@ -558,13 +557,16 @@ RED.clipboard = (function() {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function showExportNodes(mode) {
 | 
			
		||||
    /**
 | 
			
		||||
     * Show the export dialog
 | 
			
		||||
     * @params library which export destination to show
 | 
			
		||||
     * @params mode whether to default to 'auto' (default) or 'flow'
 | 
			
		||||
     **/
 | 
			
		||||
    function showExportNodes(library = 'clipboard', mode = 'auto' ) {
 | 
			
		||||
        if (disabled) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        mode = mode || "clipboard";
 | 
			
		||||
 | 
			
		||||
        dialogContainer.empty();
 | 
			
		||||
        dialogContainer.append($(exportNodesDialog));
 | 
			
		||||
 | 
			
		||||
@@ -766,12 +768,15 @@ RED.clipboard = (function() {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (mode === 'flow' && !$("#red-ui-clipboard-dialog-export-rng-flow").hasClass('disabled')) {
 | 
			
		||||
            $("#red-ui-clipboard-dialog-export-rng-flow").trigger("click");
 | 
			
		||||
        }
 | 
			
		||||
        if (format === "red-ui-clipboard-dialog-export-fmt-full") {
 | 
			
		||||
            $("#red-ui-clipboard-dialog-export-fmt-full").trigger("click");
 | 
			
		||||
        } else {
 | 
			
		||||
            $("#red-ui-clipboard-dialog-export-fmt-mini").trigger("click");
 | 
			
		||||
        }
 | 
			
		||||
        tabs.activateTab("red-ui-clipboard-dialog-export-tab-"+mode);
 | 
			
		||||
        tabs.activateTab("red-ui-clipboard-dialog-export-tab-"+library);
 | 
			
		||||
 | 
			
		||||
        var dialogHeight = 400;
 | 
			
		||||
        var winHeight = $(window).height();
 | 
			
		||||
 
 | 
			
		||||
@@ -94,8 +94,8 @@ RED.menu = (function() {
 | 
			
		||||
 | 
			
		||||
            var link = $(linkContent).appendTo(item);
 | 
			
		||||
            opt.link = link;
 | 
			
		||||
            if (typeof opt.onselect === 'string') {
 | 
			
		||||
                var shortcut = RED.keyboard.getShortcut(opt.onselect);
 | 
			
		||||
            if (typeof opt.onselect === 'string' || opt.shortcut) {
 | 
			
		||||
                var shortcut = opt.shortcut || RED.keyboard.getShortcut(opt.onselect);
 | 
			
		||||
                if (shortcut && shortcut.key) {
 | 
			
		||||
                    opt.shortcutSpan = $('<span class="red-ui-popover-key">'+RED.keyboard.formatKey(shortcut.key, true)+'</span>').appendTo(link.find(".red-ui-menu-label"));
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -141,7 +141,29 @@ RED.tabs = (function() {
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        if (options.contextmenu) {
 | 
			
		||||
            wrapper.on('contextmenu', function(evt) {
 | 
			
		||||
                let clickedTab
 | 
			
		||||
                let target = evt.target
 | 
			
		||||
                while(target.nodeName !== 'A' && target.nodeName !== 'UL' && target.nodeName !== 'BODY') {
 | 
			
		||||
                    target = target.parentNode
 | 
			
		||||
                }
 | 
			
		||||
                if (target.nodeName === 'A') {
 | 
			
		||||
                    const href = target.getAttribute('href')
 | 
			
		||||
                    if (href) {
 | 
			
		||||
                        clickedTab = tabs[href.slice(1)]
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                evt.preventDefault()
 | 
			
		||||
                evt.stopPropagation()
 | 
			
		||||
                RED.contextMenu.show({
 | 
			
		||||
                    x:evt.clientX-5,
 | 
			
		||||
                    y:evt.clientY-5,
 | 
			
		||||
                    options: options.contextmenu(clickedTab)
 | 
			
		||||
                })
 | 
			
		||||
                return false
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var scrollLeft;
 | 
			
		||||
        var scrollRight;
 | 
			
		||||
@@ -809,17 +831,17 @@ RED.tabs = (function() {
 | 
			
		||||
                    });
 | 
			
		||||
                    RED.popover.tooltip(closeLink,RED._("workspace.hideFlow"));
 | 
			
		||||
                }
 | 
			
		||||
                if (tab.hideable) {
 | 
			
		||||
                    li.addClass("red-ui-tabs-closeable")
 | 
			
		||||
                    var closeLink = $("<a/>",{href:"#",class:"red-ui-tab-close red-ui-tab-hide"}).appendTo(li);
 | 
			
		||||
                    closeLink.append('<i class="fa fa-eye" />');
 | 
			
		||||
                    closeLink.append('<i class="fa fa-eye-slash" />');
 | 
			
		||||
                    closeLink.on("click",function(event) {
 | 
			
		||||
                        event.preventDefault();
 | 
			
		||||
                        hideTab(tab.id);
 | 
			
		||||
                    });
 | 
			
		||||
                    RED.popover.tooltip(closeLink,RED._("workspace.hideFlow"));
 | 
			
		||||
                }
 | 
			
		||||
                // if (tab.hideable) {
 | 
			
		||||
                //     li.addClass("red-ui-tabs-closeable")
 | 
			
		||||
                //     var closeLink = $("<a/>",{href:"#",class:"red-ui-tab-close red-ui-tab-hide"}).appendTo(li);
 | 
			
		||||
                //     closeLink.append('<i class="fa fa-eye" />');
 | 
			
		||||
                //     closeLink.append('<i class="fa fa-eye-slash" />');
 | 
			
		||||
                //     closeLink.on("click",function(event) {
 | 
			
		||||
                //         event.preventDefault();
 | 
			
		||||
                //         hideTab(tab.id);
 | 
			
		||||
                //     });
 | 
			
		||||
                //     RED.popover.tooltip(closeLink,RED._("workspace.hideFlow"));
 | 
			
		||||
                // }
 | 
			
		||||
 | 
			
		||||
                var badges = $('<span class="red-ui-tabs-badges"></span>').appendTo(li);
 | 
			
		||||
                if (options.onselect) {
 | 
			
		||||
@@ -938,6 +960,9 @@ RED.tabs = (function() {
 | 
			
		||||
            activeIndex: function() {
 | 
			
		||||
                return ul.find("li.active").index()
 | 
			
		||||
            },
 | 
			
		||||
            getTabIndex: function (id) {
 | 
			
		||||
                return ul.find("a[href='#"+id+"']").parent().index()
 | 
			
		||||
            },
 | 
			
		||||
            contains: function(id) {
 | 
			
		||||
                return ul.find("a[href='#"+id+"']").length > 0;
 | 
			
		||||
            },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,6 @@
 | 
			
		||||
RED.contextMenu = (function () {
 | 
			
		||||
 | 
			
		||||
    let menu;
 | 
			
		||||
    function createMenu() {
 | 
			
		||||
        // menu = RED.popover.menu({
 | 
			
		||||
        //     options: [
 | 
			
		||||
        //         {
 | 
			
		||||
        //             label: 'delete selection',
 | 
			
		||||
        //             onselect: function() {
 | 
			
		||||
        //                 RED.actions.invoke('core:delete-selection')
 | 
			
		||||
        //                 RED.view.focus()
 | 
			
		||||
        //             }
 | 
			
		||||
        //         },
 | 
			
		||||
        //         { label: 'world' }
 | 
			
		||||
        //     ],
 | 
			
		||||
        //     width: 200,
 | 
			
		||||
        // })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function disposeMenu() {
 | 
			
		||||
        $(document).off("mousedown.red-ui-workspace-context-menu");
 | 
			
		||||
@@ -28,114 +13,118 @@ RED.contextMenu = (function () {
 | 
			
		||||
        if (menu) {
 | 
			
		||||
            menu.remove()
 | 
			
		||||
        }
 | 
			
		||||
        let menuItems = []
 | 
			
		||||
        if (options.options) {
 | 
			
		||||
            menuItems = options.options
 | 
			
		||||
        } else if (options.type === 'workspace') {
 | 
			
		||||
            const selection = RED.view.selection()
 | 
			
		||||
            const noSelection = !selection || Object.keys(selection).length === 0
 | 
			
		||||
            const hasSelection = (selection.nodes && selection.nodes.length > 0);
 | 
			
		||||
            const hasMultipleSelection = hasSelection && selection.nodes.length > 1;
 | 
			
		||||
            const virtulLinks = (selection.links && selection.links.filter(e => !!e.link)) || [];
 | 
			
		||||
            const wireLinks = (selection.links && selection.links.filter(e => !e.link)) || [];
 | 
			
		||||
            const hasLinks = wireLinks.length > 0;
 | 
			
		||||
            const isSingleLink = !hasSelection && hasLinks && wireLinks.length === 1
 | 
			
		||||
            const isMultipleLinks = !hasSelection && hasLinks && wireLinks.length > 1
 | 
			
		||||
            const canDelete = hasSelection || hasLinks
 | 
			
		||||
            const isGroup = hasSelection && selection.nodes.length === 1 && selection.nodes[0].type === 'group'
 | 
			
		||||
 | 
			
		||||
        const selection = RED.view.selection()
 | 
			
		||||
        const noSelection = !selection || Object.keys(selection).length === 0
 | 
			
		||||
        const hasSelection = (selection.nodes && selection.nodes.length > 0);
 | 
			
		||||
        const hasMultipleSelection = hasSelection && selection.nodes.length > 1;
 | 
			
		||||
        const virtulLinks = (selection.links && selection.links.filter(e => !!e.link)) || [];
 | 
			
		||||
        const wireLinks = (selection.links && selection.links.filter(e => !e.link)) || [];
 | 
			
		||||
        const hasLinks = wireLinks.length > 0;
 | 
			
		||||
        const isSingleLink = !hasSelection && hasLinks && wireLinks.length === 1
 | 
			
		||||
        const isMultipleLinks = !hasSelection && hasLinks && wireLinks.length > 1
 | 
			
		||||
        const canDelete = hasSelection || hasLinks
 | 
			
		||||
        const isGroup = hasSelection && selection.nodes.length === 1 && selection.nodes[0].type === 'group'
 | 
			
		||||
 | 
			
		||||
        const canRemoveFromGroup = hasSelection && !!selection.nodes[0].g
 | 
			
		||||
        const offset = $("#red-ui-workspace-chart").offset()
 | 
			
		||||
 | 
			
		||||
        let addX = options.x - offset.left + $("#red-ui-workspace-chart").scrollLeft()
 | 
			
		||||
        let addY = options.y - offset.top + $("#red-ui-workspace-chart").scrollTop()
 | 
			
		||||
 | 
			
		||||
        if (RED.view.snapGrid) {
 | 
			
		||||
            const gridSize = RED.view.gridSize()
 | 
			
		||||
            addX = gridSize * Math.floor(addX / gridSize)
 | 
			
		||||
            addY = gridSize * Math.floor(addY / gridSize)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const menuItems = [
 | 
			
		||||
            { onselect: 'core:show-action-list', onpostselect: function () { } },
 | 
			
		||||
            {
 | 
			
		||||
                label: RED._("contextMenu.insert"),
 | 
			
		||||
                options: [
 | 
			
		||||
                    {
 | 
			
		||||
                        label: RED._("contextMenu.node"),
 | 
			
		||||
                        onselect: function () {
 | 
			
		||||
                            RED.view.showQuickAddDialog({
 | 
			
		||||
                                position: [addX, addY],
 | 
			
		||||
                                touchTrigger: true,
 | 
			
		||||
                                splice: isSingleLink ? selection.links[0] : undefined,
 | 
			
		||||
                                // spliceMultiple: isMultipleLinks
 | 
			
		||||
                            })
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    (hasLinks) ? { // has least 1 wire selected
 | 
			
		||||
                        label: RED._("contextMenu.junction"),
 | 
			
		||||
                        onselect: 'core:split-wires-with-junctions',
 | 
			
		||||
                        disabled: !hasLinks
 | 
			
		||||
                    } : {
 | 
			
		||||
                        label: RED._("contextMenu.junction"),
 | 
			
		||||
                        onselect: function () {
 | 
			
		||||
                            const nn = {
 | 
			
		||||
                                _def: { defaults: {} },
 | 
			
		||||
                                type: 'junction',
 | 
			
		||||
                                z: RED.workspaces.active(),
 | 
			
		||||
                                id: RED.nodes.id(),
 | 
			
		||||
                                x: addX,
 | 
			
		||||
                                y: addY,
 | 
			
		||||
                                w: 0, h: 0,
 | 
			
		||||
                                outputs: 1,
 | 
			
		||||
                                inputs: 1,
 | 
			
		||||
                                dirty: true
 | 
			
		||||
                            }
 | 
			
		||||
                            const historyEvent = {
 | 
			
		||||
                                dirty: RED.nodes.dirty(),
 | 
			
		||||
                                t: 'add',
 | 
			
		||||
                                junctions: [nn]
 | 
			
		||||
                            }
 | 
			
		||||
                            RED.nodes.addJunction(nn);
 | 
			
		||||
                            RED.history.push(historyEvent);
 | 
			
		||||
                            RED.nodes.dirty(true);
 | 
			
		||||
                            RED.view.select({nodes: [nn] });
 | 
			
		||||
                            RED.view.redraw(true)
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        label: RED._("contextMenu.linkNodes"),
 | 
			
		||||
                        onselect: 'core:split-wire-with-link-nodes',
 | 
			
		||||
                        disabled: !hasLinks
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
            const canRemoveFromGroup = hasSelection && !!selection.nodes[0].g
 | 
			
		||||
            const offset = $("#red-ui-workspace-chart").offset()
 | 
			
		||||
 | 
			
		||||
            let addX = options.x - offset.left + $("#red-ui-workspace-chart").scrollLeft()
 | 
			
		||||
            let addY = options.y - offset.top + $("#red-ui-workspace-chart").scrollTop()
 | 
			
		||||
 | 
			
		||||
            if (RED.view.snapGrid) {
 | 
			
		||||
                const gridSize = RED.view.gridSize()
 | 
			
		||||
                addX = gridSize * Math.floor(addX / gridSize)
 | 
			
		||||
                addY = gridSize * Math.floor(addY / gridSize)
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        menuItems.push(
 | 
			
		||||
            null,
 | 
			
		||||
            { onselect: 'core:undo', disabled: RED.history.list().length === 0 },
 | 
			
		||||
            { onselect: 'core:redo', disabled: RED.history.listRedo().length === 0 },
 | 
			
		||||
            null,
 | 
			
		||||
            { onselect: 'core:cut-selection-to-internal-clipboard', label: RED._("keyboard.cutNode"), disabled: !hasSelection },
 | 
			
		||||
            { onselect: 'core:copy-selection-to-internal-clipboard', label: RED._("keyboard.copyNode"), disabled: !hasSelection },
 | 
			
		||||
            { onselect: 'core:paste-from-internal-clipboard', label: RED._("keyboard.pasteNode"), disabled: !RED.view.clipboard() },
 | 
			
		||||
            { onselect: 'core:delete-selection', disabled: !canDelete },
 | 
			
		||||
            { onselect: 'core:show-export-dialog', label: RED._("menu.label.export") },
 | 
			
		||||
            { onselect: 'core:select-all-nodes' }
 | 
			
		||||
        )
 | 
			
		||||
            menuItems.push(
 | 
			
		||||
                { onselect: 'core:show-action-list', onpostselect: function () { } },
 | 
			
		||||
                {
 | 
			
		||||
                    label: RED._("contextMenu.insert"),
 | 
			
		||||
                    options: [
 | 
			
		||||
                        {
 | 
			
		||||
                            label: RED._("contextMenu.node"),
 | 
			
		||||
                            onselect: function () {
 | 
			
		||||
                                RED.view.showQuickAddDialog({
 | 
			
		||||
                                    position: [addX, addY],
 | 
			
		||||
                                    touchTrigger: true,
 | 
			
		||||
                                    splice: isSingleLink ? selection.links[0] : undefined,
 | 
			
		||||
                                    // spliceMultiple: isMultipleLinks
 | 
			
		||||
                                })
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                        (hasLinks) ? { // has least 1 wire selected
 | 
			
		||||
                            label: RED._("contextMenu.junction"),
 | 
			
		||||
                            onselect: 'core:split-wires-with-junctions',
 | 
			
		||||
                            disabled: !hasLinks
 | 
			
		||||
                        } : {
 | 
			
		||||
                            label: RED._("contextMenu.junction"),
 | 
			
		||||
                            onselect: function () {
 | 
			
		||||
                                const nn = {
 | 
			
		||||
                                    _def: { defaults: {} },
 | 
			
		||||
                                    type: 'junction',
 | 
			
		||||
                                    z: RED.workspaces.active(),
 | 
			
		||||
                                    id: RED.nodes.id(),
 | 
			
		||||
                                    x: addX,
 | 
			
		||||
                                    y: addY,
 | 
			
		||||
                                    w: 0, h: 0,
 | 
			
		||||
                                    outputs: 1,
 | 
			
		||||
                                    inputs: 1,
 | 
			
		||||
                                    dirty: true
 | 
			
		||||
                                }
 | 
			
		||||
                                const historyEvent = {
 | 
			
		||||
                                    dirty: RED.nodes.dirty(),
 | 
			
		||||
                                    t: 'add',
 | 
			
		||||
                                    junctions: [nn]
 | 
			
		||||
                                }
 | 
			
		||||
                                RED.nodes.addJunction(nn);
 | 
			
		||||
                                RED.history.push(historyEvent);
 | 
			
		||||
                                RED.nodes.dirty(true);
 | 
			
		||||
                                RED.view.select({nodes: [nn] });
 | 
			
		||||
                                RED.view.redraw(true)
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            label: RED._("contextMenu.linkNodes"),
 | 
			
		||||
                            onselect: 'core:split-wire-with-link-nodes',
 | 
			
		||||
                            disabled: !hasLinks
 | 
			
		||||
                        }
 | 
			
		||||
                    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        if (hasSelection) {
 | 
			
		||||
            menuItems.push(
 | 
			
		||||
                null,
 | 
			
		||||
                isGroup ?
 | 
			
		||||
                    { onselect: 'core:ungroup-selection', disabled: !isGroup }
 | 
			
		||||
                    : { onselect: 'core:group-selection', disabled: !hasSelection }
 | 
			
		||||
                { onselect: 'core:undo', disabled: RED.history.list().length === 0 },
 | 
			
		||||
                { onselect: 'core:redo', disabled: RED.history.listRedo().length === 0 },
 | 
			
		||||
                null,
 | 
			
		||||
                { onselect: 'core:cut-selection-to-internal-clipboard', label: RED._("keyboard.cutNode"), disabled: !hasSelection },
 | 
			
		||||
                { onselect: 'core:copy-selection-to-internal-clipboard', label: RED._("keyboard.copyNode"), disabled: !hasSelection },
 | 
			
		||||
                { onselect: 'core:paste-from-internal-clipboard', label: RED._("keyboard.pasteNode"), disabled: !RED.view.clipboard() },
 | 
			
		||||
                { onselect: 'core:delete-selection', disabled: !canDelete },
 | 
			
		||||
                { onselect: 'core:show-export-dialog', label: RED._("menu.label.export") },
 | 
			
		||||
                { onselect: 'core:select-all-nodes' }
 | 
			
		||||
            )
 | 
			
		||||
            if (canRemoveFromGroup) {
 | 
			
		||||
                menuItems.push({ onselect: 'core:remove-selection-from-group', label: RED._("menu.label.groupRemoveSelection") })
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (hasSelection) {
 | 
			
		||||
                menuItems.push(
 | 
			
		||||
                    null,
 | 
			
		||||
                    isGroup ?
 | 
			
		||||
                        { onselect: 'core:ungroup-selection', disabled: !isGroup }
 | 
			
		||||
                        : { onselect: 'core:group-selection', disabled: !hasSelection }
 | 
			
		||||
                )
 | 
			
		||||
                if (canRemoveFromGroup) {
 | 
			
		||||
                    menuItems.push({ onselect: 'core:remove-selection-from-group', label: RED._("menu.label.groupRemoveSelection") })
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var direction = "right";
 | 
			
		||||
@@ -144,7 +133,7 @@ RED.contextMenu = (function () {
 | 
			
		||||
            ($(window).width() -MENU_WIDTH)) {
 | 
			
		||||
            direction = "left";
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        menu = RED.menu.init({
 | 
			
		||||
            direction: direction,
 | 
			
		||||
            onpreselect: function() {
 | 
			
		||||
 
 | 
			
		||||
@@ -211,6 +211,7 @@ RED.view = (function() {
 | 
			
		||||
            evt.preventDefault()
 | 
			
		||||
            evt.stopPropagation()
 | 
			
		||||
            RED.contextMenu.show({
 | 
			
		||||
                type: 'workspace',
 | 
			
		||||
                x:evt.clientX-5,
 | 
			
		||||
                y:evt.clientY-5
 | 
			
		||||
            })
 | 
			
		||||
 
 | 
			
		||||
@@ -126,6 +126,113 @@ RED.workspaces = (function() {
 | 
			
		||||
 | 
			
		||||
    var workspace_tabs;
 | 
			
		||||
    var workspaceTabCount = 0;
 | 
			
		||||
 | 
			
		||||
    function getMenuItems(isMenuButton, tab) {
 | 
			
		||||
        let hiddenFlows = new Set()
 | 
			
		||||
        for (let i = 0; i < hideStack.length; i++) {
 | 
			
		||||
            let ids = hideStack[i]
 | 
			
		||||
            if (!Array.isArray(ids)) {
 | 
			
		||||
                ids = [ids]
 | 
			
		||||
            }
 | 
			
		||||
            ids.forEach(id => {
 | 
			
		||||
                if (RED.nodes.workspace(id)) {
 | 
			
		||||
                    hiddenFlows.add(id)
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
        const hiddenflowCount = hiddenFlows.size;
 | 
			
		||||
 | 
			
		||||
        var menuItems = []
 | 
			
		||||
        if (isMenuButton) {
 | 
			
		||||
            menuItems.push({
 | 
			
		||||
                id:"red-ui-tabs-menu-option-search-flows",
 | 
			
		||||
                label: RED._("workspace.listFlows"),
 | 
			
		||||
                onselect: "core:list-flows"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                id:"red-ui-tabs-menu-option-search-subflows",
 | 
			
		||||
                label: RED._("workspace.listSubflows"),
 | 
			
		||||
                onselect: "core:list-subflows"
 | 
			
		||||
            },
 | 
			
		||||
            null)
 | 
			
		||||
        }
 | 
			
		||||
        menuItems.push(
 | 
			
		||||
            {
 | 
			
		||||
                id:"red-ui-tabs-menu-option-add-flow",
 | 
			
		||||
                label: RED._("workspace.addFlow"),
 | 
			
		||||
                onselect: "core:add-flow"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                id:"red-ui-tabs-menu-option-add-flow-right",
 | 
			
		||||
                label: RED._("workspace.addFlowToRight"),
 | 
			
		||||
                shortcut: RED.keyboard.getShortcut("core:add-flow-to-right"),
 | 
			
		||||
                onselect: function() {
 | 
			
		||||
                    RED.actions.invoke("core:add-flow-to-right", tab)
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            null,
 | 
			
		||||
            {
 | 
			
		||||
                id:"red-ui-tabs-menu-option-add-hide-flows",
 | 
			
		||||
                label: RED._("workspace.hideFlow"),
 | 
			
		||||
                shortcut: RED.keyboard.getShortcut("core:hide-flow"),
 | 
			
		||||
                onselect: function() {
 | 
			
		||||
                    RED.actions.invoke("core:hide-flow", tab)
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                id:"red-ui-tabs-menu-option-add-hide-other-flows",
 | 
			
		||||
                label: RED._("workspace.hideOtherFlows"),
 | 
			
		||||
                shortcut: RED.keyboard.getShortcut("core:hide-other-flows"),
 | 
			
		||||
                onselect: function() {
 | 
			
		||||
                    RED.actions.invoke("core:hide-other-flows", tab)
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                id:"red-ui-tabs-menu-option-add-hide-all-flows",
 | 
			
		||||
                label: RED._("workspace.hideAllFlows"),
 | 
			
		||||
                onselect: "core:hide-all-flows"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                id:"red-ui-tabs-menu-option-add-show-all-flows",
 | 
			
		||||
                disabled: hiddenflowCount === 0,
 | 
			
		||||
                label: RED._("workspace.showAllFlows", { count: hiddenflowCount }),
 | 
			
		||||
                onselect: "core:show-all-flows"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                id:"red-ui-tabs-menu-option-add-show-last-flow",
 | 
			
		||||
                disabled: hideStack.length === 0,
 | 
			
		||||
                label: RED._("workspace.showLastHiddenFlow"),
 | 
			
		||||
                onselect: "core:show-last-hidden-flow"
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
        if (tab) {
 | 
			
		||||
            menuItems.push(
 | 
			
		||||
                null,
 | 
			
		||||
                {
 | 
			
		||||
                    label: RED._("common.label.delete"),
 | 
			
		||||
                    disabled: tab.type !== 'tab',
 | 
			
		||||
                    onselect: function() {
 | 
			
		||||
                        RED.workspaces.delete(tab)
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    label: RED._("menu.label.export"),
 | 
			
		||||
                    shortcut: RED.keyboard.getShortcut("core:show-export-dialog"),
 | 
			
		||||
                    onselect: function() {
 | 
			
		||||
                        RED.workspaces.show(tab.id)
 | 
			
		||||
                        RED.actions.invoke('core:show-export-dialog', null, 'flow')
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        // if (isMenuButton && hiddenflowCount > 0) {
 | 
			
		||||
        //     menuItems.unshift({
 | 
			
		||||
        //         label: RED._("workspace.hiddenFlows",{count: hiddenflowCount}),
 | 
			
		||||
        //         onselect: "core:list-hidden-flows"
 | 
			
		||||
        //     })
 | 
			
		||||
        // }
 | 
			
		||||
        return menuItems;
 | 
			
		||||
    }
 | 
			
		||||
    function createWorkspaceTabs() {
 | 
			
		||||
        workspace_tabs = RED.tabs.create({
 | 
			
		||||
            id: "red-ui-workspace-tabs",
 | 
			
		||||
@@ -214,12 +321,12 @@ RED.workspaces = (function() {
 | 
			
		||||
            },
 | 
			
		||||
            onhide: function(tab) {
 | 
			
		||||
                hideStack.push(tab.id);
 | 
			
		||||
 | 
			
		||||
                var hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
 | 
			
		||||
                hiddenTabs[tab.id] = true;
 | 
			
		||||
                RED.settings.setLocal("hiddenTabs",JSON.stringify(hiddenTabs));
 | 
			
		||||
 | 
			
		||||
                RED.events.emit("workspace:hide",{workspace: tab.id})
 | 
			
		||||
                if (tab.type === "tab") {
 | 
			
		||||
                    var hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
 | 
			
		||||
                    hiddenTabs[tab.id] = true;
 | 
			
		||||
                    RED.settings.setLocal("hiddenTabs",JSON.stringify(hiddenTabs));
 | 
			
		||||
                    RED.events.emit("workspace:hide",{workspace: tab.id})
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            onshow: function(tab) {
 | 
			
		||||
                removeFromHideStack(tab.id);
 | 
			
		||||
@@ -234,77 +341,8 @@ RED.workspaces = (function() {
 | 
			
		||||
            scrollable: true,
 | 
			
		||||
            addButton: "core:add-flow",
 | 
			
		||||
            addButtonCaption: RED._("workspace.addFlow"),
 | 
			
		||||
            menu: function() {
 | 
			
		||||
                var menuItems = [
 | 
			
		||||
                    {
 | 
			
		||||
                        id:"red-ui-tabs-menu-option-search-flows",
 | 
			
		||||
                        label: RED._("workspace.listFlows"),
 | 
			
		||||
                        onselect: "core:list-flows"
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        id:"red-ui-tabs-menu-option-search-subflows",
 | 
			
		||||
                        label: RED._("workspace.listSubflows"),
 | 
			
		||||
                        onselect: "core:list-subflows"
 | 
			
		||||
                    },
 | 
			
		||||
                    null,
 | 
			
		||||
                    {
 | 
			
		||||
                        id:"red-ui-tabs-menu-option-add-flow",
 | 
			
		||||
                        label: RED._("workspace.addFlow"),
 | 
			
		||||
                        onselect: "core:add-flow"
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        id:"red-ui-tabs-menu-option-add-flow-right",
 | 
			
		||||
                        label: RED._("workspace.addFlowToRight"),
 | 
			
		||||
                        onselect: "core:add-flow-to-right"
 | 
			
		||||
                    },
 | 
			
		||||
                    null,
 | 
			
		||||
                    {
 | 
			
		||||
                        id:"red-ui-tabs-menu-option-add-hide-flows",
 | 
			
		||||
                        label: RED._("workspace.hideFlow"),
 | 
			
		||||
                        onselect: "core:hide-flow"
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        id:"red-ui-tabs-menu-option-add-hide-other-flows",
 | 
			
		||||
                        label: RED._("workspace.hideOtherFlows"),
 | 
			
		||||
                        onselect: "core:hide-other-flows"
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        id:"red-ui-tabs-menu-option-add-show-all-flows",
 | 
			
		||||
                        label: RED._("workspace.showAllFlows"),
 | 
			
		||||
                        onselect: "core:show-all-flows"
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        id:"red-ui-tabs-menu-option-add-hide-all-flows",
 | 
			
		||||
                        label: RED._("workspace.hideAllFlows"),
 | 
			
		||||
                        onselect: "core:hide-all-flows"
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        id:"red-ui-tabs-menu-option-add-show-last-flow",
 | 
			
		||||
                        label: RED._("workspace.showLastHiddenFlow"),
 | 
			
		||||
                        onselect: "core:show-last-hidden-flow"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
                let hiddenFlows = new Set()
 | 
			
		||||
                for (let i = 0; i < hideStack.length; i++) {
 | 
			
		||||
                    let ids = hideStack[i]
 | 
			
		||||
                    if (!Array.isArray(ids)) {
 | 
			
		||||
                        ids = [ids]
 | 
			
		||||
                    }
 | 
			
		||||
                    ids.forEach(id => {
 | 
			
		||||
                        if (RED.nodes.workspace(id)) {
 | 
			
		||||
                            hiddenFlows.add(id)
 | 
			
		||||
                        }
 | 
			
		||||
                    })
 | 
			
		||||
                }
 | 
			
		||||
                const flowCount = hiddenFlows.size;
 | 
			
		||||
                if (flowCount > 0) {
 | 
			
		||||
                    menuItems.unshift({
 | 
			
		||||
                        label: RED._("workspace.hiddenFlows",{count: flowCount}),
 | 
			
		||||
                        onselect: "core:list-hidden-flows"
 | 
			
		||||
                    })
 | 
			
		||||
                }
 | 
			
		||||
                return menuItems;
 | 
			
		||||
            }
 | 
			
		||||
            menu: function() { return getMenuItems(true) },
 | 
			
		||||
            contextmenu: function(tab) { return getMenuItems(false, tab) }
 | 
			
		||||
        });
 | 
			
		||||
        workspaceTabCount = 0;
 | 
			
		||||
    }
 | 
			
		||||
@@ -355,16 +393,29 @@ RED.workspaces = (function() {
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        RED.actions.add("core:add-flow",function(opts) { addWorkspace(undefined,undefined,opts?opts.index:undefined)});
 | 
			
		||||
        RED.actions.add("core:add-flow-to-right",function(opts) { addWorkspace(undefined,undefined,workspace_tabs.activeIndex()+1)});
 | 
			
		||||
        RED.actions.add("core:add-flow-to-right",function(workspace) {
 | 
			
		||||
            let index
 | 
			
		||||
            if (workspace) {
 | 
			
		||||
                index = workspace_tabs.getTabIndex(workspace.id)+1
 | 
			
		||||
            } else {
 | 
			
		||||
                index = workspace_tabs.activeIndex()+1
 | 
			
		||||
            }
 | 
			
		||||
            addWorkspace(undefined,undefined,index)
 | 
			
		||||
        });
 | 
			
		||||
        RED.actions.add("core:edit-flow",editWorkspace);
 | 
			
		||||
        RED.actions.add("core:remove-flow",removeWorkspace);
 | 
			
		||||
        RED.actions.add("core:enable-flow",enableWorkspace);
 | 
			
		||||
        RED.actions.add("core:disable-flow",disableWorkspace);
 | 
			
		||||
 | 
			
		||||
        RED.actions.add("core:hide-flow", function() {
 | 
			
		||||
            var selection = workspace_tabs.selection();
 | 
			
		||||
            if (selection.length === 0) {
 | 
			
		||||
                selection = [{id:activeWorkspace}]
 | 
			
		||||
        RED.actions.add("core:hide-flow", function(workspace) {
 | 
			
		||||
            let selection
 | 
			
		||||
            if (workspace) {
 | 
			
		||||
                selection = [workspace]
 | 
			
		||||
            } else {
 | 
			
		||||
                selection = workspace_tabs.selection();
 | 
			
		||||
                if (selection.length === 0) {
 | 
			
		||||
                    selection = [{id:activeWorkspace}]
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            var hiddenTabs = [];
 | 
			
		||||
            selection.forEach(function(ws) {
 | 
			
		||||
@@ -378,10 +429,15 @@ RED.workspaces = (function() {
 | 
			
		||||
            workspace_tabs.clearSelection();
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        RED.actions.add("core:hide-other-flows", function() {
 | 
			
		||||
            var selection = workspace_tabs.selection();
 | 
			
		||||
            if (selection.length === 0) {
 | 
			
		||||
                selection = [{id:activeWorkspace}]
 | 
			
		||||
        RED.actions.add("core:hide-other-flows", function(workspace) {
 | 
			
		||||
            let selection
 | 
			
		||||
            if (workspace) {
 | 
			
		||||
                selection = [workspace]
 | 
			
		||||
            } else {
 | 
			
		||||
                selection = workspace_tabs.selection();
 | 
			
		||||
                if (selection.length === 0) {
 | 
			
		||||
                    selection = [{id:activeWorkspace}]
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            var selected = new Set(selection.map(function(ws) { return ws.id }))
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user