Keep active tab state in one place (manager) (#683)

With this change TabsBase objects will retain a reference to the TabsManager they belong to, so they can ask it if they're the active tab or not.

This removes the possibility for bugs like activating an unmanaged tab, or having more than one active tab, etc.
This commit is contained in:
Jordi Bunster 2021-04-18 17:48:39 -07:00 committed by GitHub
parent a9fd01f9b4
commit e0060b12e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 74 additions and 143 deletions

View File

@ -276,7 +276,6 @@ export interface TabOptions {
tabKind: CollectionTabKind;
title: string;
tabPath: string;
isActive: ko.Observable<boolean>;
hashLocation: string;
onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]) => void;
isTabsContentExpanded?: ko.Observable<boolean>;

View File

@ -39,7 +39,6 @@ describe("SettingsComponent", () => {
tabPath: "",
node: undefined,
hashLocation: "settings",
isActive: ko.observable(false),
onUpdateTabsButtons: undefined,
}),
};

View File

@ -559,6 +559,12 @@ export default class Explorer {
});
this.tabsManager = params?.tabsManager ?? new TabsManager();
this.tabsManager.openedTabs.subscribe((tabs) => {
if (tabs.length === 0) {
this.selectedNode(undefined);
this.onUpdateTabsButtons([]);
}
});
this._panes = [
this.addDatabasePane,
@ -1570,7 +1576,6 @@ export default class Explorer {
collection: null,
masterKey: userContext.masterKey || "",
hashLocation: "notebooks",
isActive: ko.observable(false),
isTabsContentExpanded: ko.observable(true),
onLoadStartKey: null,
onUpdateTabsButtons: this.onUpdateTabsButtons,
@ -1976,7 +1981,6 @@ export default class Explorer {
tabPath: title,
collection: null,
hashLocation: hashLocation,
isActive: ko.observable(false),
isTabsContentExpanded: ko.observable(true),
onLoadStartKey: null,
onUpdateTabsButtons: this.onUpdateTabsButtons,

View File

@ -23,8 +23,8 @@ export class CommandBarComponentAdapter implements ReactAdapter {
constructor(container: Explorer) {
this.container = container;
this.tabsButtons = [];
this.isNotebookTabActive = ko.computed(() =>
container.tabsManager.isTabActive(ViewModels.CollectionTabKind.NotebookV2)
this.isNotebookTabActive = ko.computed(
() => container.tabsManager.activeTab()?.tabKind === ViewModels.CollectionTabKind.NotebookV2
);
// These are the parameters watched by the react binding that will trigger a renderComponent() if one of the ko mutates

View File

@ -16,8 +16,6 @@ describe("Documents tab", () => {
title: "",
tabPath: "",
hashLocation: "",
isActive: ko.observable<boolean>(false),
onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]): void => {},
});
@ -89,8 +87,6 @@ describe("Documents tab", () => {
title: "",
tabPath: "",
hashLocation: "",
isActive: ko.observable<boolean>(false),
onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]): void => {},
});
@ -106,8 +102,6 @@ describe("Documents tab", () => {
title: "",
tabPath: "",
hashLocation: "",
isActive: ko.observable<boolean>(false),
onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]): void => {},
});
@ -123,8 +117,6 @@ describe("Documents tab", () => {
title: "",
tabPath: "",
hashLocation: "",
isActive: ko.observable<boolean>(false),
onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]): void => {},
});
@ -140,8 +132,6 @@ describe("Documents tab", () => {
title: "",
tabPath: "",
hashLocation: "",
isActive: ko.observable<boolean>(false),
onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]): void => {},
});
@ -157,8 +147,6 @@ describe("Documents tab", () => {
title: "",
tabPath: "",
hashLocation: "",
isActive: ko.observable<boolean>(false),
onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]): void => {},
});

View File

@ -88,7 +88,6 @@ export default class NotebookTabV2 extends TabsBase {
public onCloseTabButtonClick(): Q.Promise<any> {
const cleanup = () => {
this.notebookComponentAdapter.notebookShutdown();
this.isActive(false);
super.onCloseTabButtonClick();
};

View File

@ -25,7 +25,6 @@ describe("Query Tab", () => {
database: database,
title: "",
tabPath: "",
isActive: ko.observable<boolean>(false),
hashLocation: "",
onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]): void => {},
});

View File

@ -1,15 +1,16 @@
import * as ko from "knockout";
import Q from "q";
import * as Constants from "../../Common/Constants";
import * as ViewModels from "../../Contracts/ViewModels";
import * as DataModels from "../../Contracts/DataModels";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import { RouteHandler } from "../../RouteHandlers/RouteHandler";
import { WaitsForTemplateViewModel } from "../WaitsForTemplateViewModel";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import * as ThemeUtility from "../../Common/ThemeUtility";
import Explorer from "../Explorer";
import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels";
import { RouteHandler } from "../../RouteHandlers/RouteHandler";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
import Explorer from "../Explorer";
import { WaitsForTemplateViewModel } from "../WaitsForTemplateViewModel";
import { TabsManager } from "./TabsManager";
// TODO: Use specific actions for logging telemetry data
export default class TabsBase extends WaitsForTemplateViewModel {
@ -20,18 +21,16 @@ export default class TabsBase extends WaitsForTemplateViewModel {
public database: ViewModels.Database;
public rid: string;
public hasFocus: ko.Observable<boolean>;
public isActive: ko.Observable<boolean>;
public isMouseOver: ko.Observable<boolean>;
public tabId: string;
public tabKind: ViewModels.CollectionTabKind;
public tabTitle: ko.Observable<string>;
public tabPath: ko.Observable<string>;
public closeButtonTabIndex: ko.Computed<number>;
public errorDetailsTabIndex: ko.Computed<number>;
public hashLocation: ko.Observable<string>;
public isExecutionError: ko.Observable<boolean>;
public isExecuting: ko.Observable<boolean>;
public pendingNotification?: ko.Observable<DataModels.Notification>;
public manager?: TabsManager;
protected _theme: string;
public onLoadStartKey: number;
@ -46,7 +45,6 @@ export default class TabsBase extends WaitsForTemplateViewModel {
this.database = options.database;
this.rid = options.rid || (this.collection && this.collection.rid) || "";
this.hasFocus = ko.observable<boolean>(false);
this.isActive = options.isActive || ko.observable<boolean>(false);
this.isMouseOver = ko.observable<boolean>(false);
this.tabId = `tab${id}`;
this.tabKind = options.tabKind;
@ -55,21 +53,12 @@ export default class TabsBase extends WaitsForTemplateViewModel {
(options.tabPath && ko.observable<string>(options.tabPath)) ||
(this.collection &&
ko.observable<string>(`${this.collection.databaseId}>${this.collection.id()}>${this.tabTitle()}`));
this.closeButtonTabIndex = ko.computed<number>(() => (this.isActive() ? 0 : null));
this.errorDetailsTabIndex = ko.computed<number>(() => (this.isActive() ? 0 : null));
this.isExecutionError = ko.observable<boolean>(false);
this.isExecuting = ko.observable<boolean>(false);
this.pendingNotification = ko.observable<DataModels.Notification>(undefined);
this.onLoadStartKey = options.onLoadStartKey;
this.hashLocation = ko.observable<string>(options.hashLocation || "");
this.hashLocation.subscribe((newLocation: string) => this.updateGlobalHash(newLocation));
this.isActive.subscribe((isActive: boolean) => {
if (isActive) {
this.onActivate();
}
});
this.closeTabButton = {
enabled: ko.computed<boolean>(() => {
return true;
@ -82,12 +71,9 @@ export default class TabsBase extends WaitsForTemplateViewModel {
}
public onCloseTabButtonClick(): void {
const explorer = this.getContainer();
explorer.tabsManager.closeTab(this.tabId, explorer);
this.manager?.closeTab(this);
TelemetryProcessor.trace(Action.Tab, ActionModifiers.Close, {
tabName: this.constructor.name,
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle(),
tabId: this.tabId,
@ -95,7 +81,7 @@ export default class TabsBase extends WaitsForTemplateViewModel {
}
public onTabClick(): void {
this.getContainer().tabsManager.activateTab(this);
this.manager?.activateTab(this);
}
protected updateSelectedNode(): void {
@ -127,6 +113,11 @@ export default class TabsBase extends WaitsForTemplateViewModel {
return this.onSpaceOrEnterKeyPress(event, () => this.onCloseTabButtonClick());
};
/** @deprecated this is no longer observable, bind to comparisons with manager.activeTab() instead */
public isActive() {
return this === this.manager?.activeTab();
}
public onActivate(): void {
this.updateSelectedNode();
if (!!this.collection) {

View File

@ -1,4 +1,8 @@
<div id="content" class="flexContainer hideOverflows" data-bind="visible: openedTabs && openedTabs().length > 0">
<div
id="content"
class="flexContainer hideOverflows"
data-bind="visible: activeTab() && openedTabs && openedTabs().length > 0"
>
<!-- Tabs - Start -->
<div class="nav-tabs-margin">
<ul class="nav nav-tabs level navTabHeight" id="navTabs" role="tablist">
@ -8,12 +12,12 @@
data-bind="
attr: {
title: $data.tabPath,
'aria-selected': $data.isActive,
'aria-expanded': $data.isActive,
'aria-selected': $parent.activeTab() === $data,
'aria-expanded': $parent.activeTab() === $data,
'aria-controls': $data.tabId
},
css:{
active: $data.isActive
active: $parent.activeTab() === $data
},
hasFocus: $data.hasFocus,
event: { keypress: onKeyPressActivate },
@ -33,8 +37,8 @@
data-bind="
click: onErrorDetailsClick,
event: { keypress: onErrorDetailsKeyPress },
attr: { tabindex: errorDetailsTabIndex },
css: { actionsEnabled: isActive },
attr: { tabindex: $parent.activeTab() === $data ? 0 : null },
css: { actionsEnabled: $parent.activeTab() === $data },
visible: isExecutionError"
>
<span class="errorIcon"></span>
@ -56,11 +60,14 @@
data-bind="
click: $data.onCloseTabButtonClick,
event: { keypress: onKeyPressClose },
attr: { tabindex: $data.closeButtonTabIndex },
visible: $data.isActive() || $data.isMouseOver()"
attr: { tabindex: $parent.activeTab() === $data ? 0 : null },
visible: $parent.activeTab() === $data || $data.isMouseOver()"
title="Close"
>
<span class="tabIcon close-Icon" data-bind="visible: $data.isActive() || $data.isMouseOver()">
<span
class="tabIcon close-Icon"
data-bind="visible: $parent.activeTab() === $data || $data.isMouseOver()"
>
<img src="../../../images/close-black.svg" title="Close" alt="Close" />
</span>
</span>
@ -77,7 +84,7 @@
<!-- Tabs Panes -- Start -->
<div class="tabPanesContainer">
<!-- ko foreach: openedTabs -->
<div class="tabs-container" data-bind="visible: $data.isActive">
<div class="tabs-container" data-bind="visible: $parent.activeTab() === $data">
<span
data-bind="class: $data.constructor.component.name, component: { name: $data.constructor.component.name, params: $data }"
></span>

View File

@ -1,11 +1,11 @@
import * as ko from "knockout";
import * as ViewModels from "../../Contracts/ViewModels";
import * as DataModels from "../../Contracts/DataModels";
import { TabsManager } from "./TabsManager";
import DocumentsTab from "./DocumentsTab";
import * as ViewModels from "../../Contracts/ViewModels";
import Explorer from "../Explorer";
import QueryTab from "./QueryTab";
import DocumentId from "../Tree/DocumentId";
import DocumentsTab from "./DocumentsTab";
import QueryTab from "./QueryTab";
import { TabsManager } from "./TabsManager";
describe("Tabs manager tests", () => {
let tabsManager: TabsManager;
@ -50,7 +50,6 @@ describe("Tabs manager tests", () => {
database,
title: "",
tabPath: "",
isActive: ko.observable<boolean>(false),
hashLocation: "",
onUpdateTabsButtons: undefined,
});
@ -63,7 +62,6 @@ describe("Tabs manager tests", () => {
title: "",
tabPath: "",
hashLocation: "",
isActive: ko.observable<boolean>(false),
onUpdateTabsButtons: undefined,
});
@ -72,10 +70,7 @@ describe("Tabs manager tests", () => {
documentsTab.tabId = "2";
});
beforeEach(() => {
tabsManager = new TabsManager();
explorer.tabsManager = tabsManager;
});
beforeEach(() => (tabsManager = new TabsManager()));
it("open new tabs", () => {
tabsManager.activateNewTab(queryTab);
@ -122,7 +117,7 @@ describe("Tabs manager tests", () => {
tabsManager.activateNewTab(queryTab);
tabsManager.activateNewTab(documentsTab);
tabsManager.closeTab(documentsTab.tabId, explorer);
tabsManager.closeTab(documentsTab);
expect(tabsManager.openedTabs().length).toBe(1);
expect(tabsManager.openedTabs()[0]).toEqual(queryTab);
expect(tabsManager.activeTab()).toEqual(queryTab);

View File

@ -1,16 +1,10 @@
import * as ko from "knockout";
import * as ViewModels from "../../Contracts/ViewModels";
import Explorer from "../Explorer";
import TabsBase from "./TabsBase";
export class TabsManager {
public openedTabs: ko.ObservableArray<TabsBase>;
public activeTab: ko.Observable<TabsBase>;
constructor() {
this.openedTabs = ko.observableArray<TabsBase>([]);
this.activeTab = ko.observable<TabsBase>();
}
public openedTabs = ko.observableArray<TabsBase>([]);
public activeTab = ko.observable<TabsBase>();
public activateNewTab(tab: TabsBase): void {
this.openedTabs.push(tab);
@ -18,66 +12,43 @@ export class TabsManager {
}
public activateTab(tab: TabsBase): void {
this.activeTab() && this.activeTab().isActive(false);
tab.isActive(true);
this.activeTab(tab);
if (this.openedTabs().includes(tab)) {
tab.manager = this;
this.activeTab(tab);
tab.onActivate();
}
}
public getTabs(tabKind: ViewModels.CollectionTabKind, comparator?: (tab: TabsBase) => boolean): TabsBase[] {
return this.openedTabs().filter((openedTab: TabsBase) => {
return openedTab.tabKind === tabKind && (!comparator || comparator(openedTab));
});
return this.openedTabs().filter((tab) => tab.tabKind === tabKind && (!comparator || comparator(tab)));
}
public refreshActiveTab(comparator: (tab: TabsBase) => boolean): void {
// ensures that the tab selects/highlights the right node based on resource tree expand/collapse state
this.openedTabs().forEach((tab: TabsBase) => {
if (comparator(tab) && tab.isActive()) {
tab.onActivate();
}
});
}
public removeTabById(tabId: string): void {
this.openedTabs.remove((tab: TabsBase) => tab.tabId === tabId);
}
public removeTabByComparator(comparator: (tab: TabsBase) => boolean): void {
this.openedTabs.remove((tab: TabsBase) => comparator(tab));
this.activeTab() && comparator(this.activeTab()) && this.activeTab().onActivate();
}
public closeTabsByComparator(comparator: (tab: TabsBase) => boolean): void {
this.activeTab() && this.activeTab().isActive(false);
this.activeTab(undefined);
this.openedTabs().forEach((tab: TabsBase) => {
if (comparator(tab)) {
tab.onCloseTabButtonClick();
}
});
this.openedTabs()
.filter(comparator)
.forEach((tab) => tab.onCloseTabButtonClick());
}
public closeTabs(): void {
this.openedTabs([]);
}
public closeTab(tabId: string, explorer: Explorer): void {
const tabIndex: number = this.openedTabs().findIndex((tab: TabsBase) => tab.tabId === tabId);
public closeTab(tab: TabsBase): void {
const tabIndex = this.openedTabs().indexOf(tab);
if (tabIndex !== -1) {
const tabToActive: TabsBase = this.openedTabs()[tabIndex + 1] || this.openedTabs()[tabIndex - 1];
this.openedTabs()[tabIndex].isActive(false);
this.removeTabById(tabId);
if (tabToActive) {
tabToActive.isActive(true);
this.activeTab(tabToActive);
} else {
explorer.selectedNode(undefined);
explorer.onUpdateTabsButtons([]);
this.openedTabs.remove(tab);
tab.manager = undefined;
if (this.openedTabs().length === 0) {
this.activeTab(undefined);
}
if (tab === this.activeTab()) {
const tabToTheRight = this.openedTabs()[tabIndex];
const lastOpenTab = this.openedTabs()[this.openedTabs().length - 1];
this.activateTab(tabToTheRight ?? lastOpenTab);
}
}
}
public isTabActive(tabKind: ViewModels.CollectionTabKind): boolean {
return this.activeTab() && this.activeTab().tabKind === tabKind;
}
}

View File

@ -301,7 +301,6 @@ export default class Collection implements ViewModels.Collection {
documentIds: ko.observableArray<DocumentId>([]),
tabKind: ViewModels.CollectionTabKind.Documents,
title: "Items",
isActive: ko.observable<boolean>(false),
collection: this,
node: this,
tabPath: `${this.databaseId}>${this.id()}>Documents`,
@ -349,7 +348,6 @@ export default class Collection implements ViewModels.Collection {
conflictIds: ko.observableArray<ConflictId>([]),
tabKind: ViewModels.CollectionTabKind.Conflicts,
title: "Conflicts",
isActive: ko.observable<boolean>(false),
collection: this,
node: this,
tabPath: `${this.databaseId}>${this.id()}>Conflicts`,
@ -406,12 +404,9 @@ export default class Collection implements ViewModels.Collection {
tabKind: ViewModels.CollectionTabKind.QueryTables,
title: title,
tabPath: "",
collection: this,
node: this,
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/entities`,
isActive: ko.observable(false),
onLoadStartKey: startKey,
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
});
@ -463,7 +458,6 @@ export default class Collection implements ViewModels.Collection {
collectionPartitionKeyProperty: this.partitionKeyProperty,
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/graphs`,
collectionId: this.id(),
isActive: ko.observable(false),
databaseId: this.databaseId,
isTabsContentExpanded: this.container.isTabsContentExpanded,
onLoadStartKey: startKey,
@ -513,7 +507,6 @@ export default class Collection implements ViewModels.Collection {
collection: this,
node: this,
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/mongoDocuments`,
isActive: ko.observable(false),
onLoadStartKey: startKey,
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
});
@ -556,7 +549,6 @@ export default class Collection implements ViewModels.Collection {
collection: this,
node: this,
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/settings`,
isActive: ko.observable(false),
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
};
@ -599,7 +591,6 @@ export default class Collection implements ViewModels.Collection {
collection: this,
node: this,
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/query`,
isActive: ko.observable(false),
queryText: queryText,
partitionKey: collection.partitionKey,
onLoadStartKey: startKey,
@ -629,7 +620,6 @@ export default class Collection implements ViewModels.Collection {
collection: this,
node: this,
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/mongoQuery`,
isActive: ko.observable(false),
partitionKey: collection.partitionKey,
onLoadStartKey: startKey,
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
@ -661,7 +651,6 @@ export default class Collection implements ViewModels.Collection {
collectionPartitionKeyProperty: this.partitionKeyProperty,
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/graphs`,
collectionId: this.id(),
isActive: ko.observable(false),
databaseId: this.databaseId,
isTabsContentExpanded: this.container.isTabsContentExpanded,
onLoadStartKey: startKey,
@ -680,7 +669,6 @@ export default class Collection implements ViewModels.Collection {
collection: this,
node: this,
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/mongoShell`,
isActive: ko.observable(false),
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
});

View File

@ -78,7 +78,6 @@ export default class Database implements ViewModels.Database {
rid: this.rid,
database: this,
hashLocation: `${Constants.HashRoutePrefixes.databasesWithId(this.id())}/settings`,
isActive: ko.observable(false),
onLoadStartKey: startKey,
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
};

View File

@ -92,7 +92,6 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
collection: this,
node: this,
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/query`,
isActive: ko.observable(false),
queryText: queryText,
partitionKey: collection.partitionKey,
resourceTokenPartitionKey: this.container.resourceTokenPartitionKey(),
@ -139,7 +138,6 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
documentIds: ko.observableArray<DocumentId>([]),
tabKind: ViewModels.CollectionTabKind.Documents,
title: "Items",
isActive: ko.observable<boolean>(false),
collection: this,
node: this,
tabPath: `${this.databaseId}>${this.id()}>Documents`,

View File

@ -76,7 +76,6 @@ export default class StoredProcedure {
collection: source,
node: source,
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(source.databaseId, source.id())}/sproc`,
isActive: ko.observable(false),
onUpdateTabsButtons: source.container.onUpdateTabsButtons,
});
@ -123,7 +122,6 @@ export default class StoredProcedure {
this.collection.databaseId,
this.collection.id()
)}/sprocs/${this.id()}`,
isActive: ko.observable(false),
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
});
@ -138,7 +136,7 @@ export default class StoredProcedure {
deleteStoredProcedure(this.collection.databaseId, this.collection.id(), this.id()).then(
() => {
this.container.tabsManager.removeTabByComparator((tab: TabsBase) => tab.node && tab.node.rid === this.rid);
this.container.tabsManager.closeTabsByComparator((tab: TabsBase) => tab.node && tab.node.rid === this.rid);
this.collection.children.remove(this);
},
(reason) => {}

View File

@ -58,7 +58,6 @@ export default class Trigger {
collection: source,
node: source,
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(source.databaseId, source.id())}/trigger`,
isActive: ko.observable(false),
onUpdateTabsButtons: source.container.onUpdateTabsButtons,
});
@ -98,7 +97,6 @@ export default class Trigger {
this.collection.databaseId,
this.collection.id()
)}/triggers/${this.id()}`,
isActive: ko.observable(false),
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
});
@ -113,7 +111,7 @@ export default class Trigger {
deleteTrigger(this.collection.databaseId, this.collection.id(), this.id()).then(
() => {
this.container.tabsManager.removeTabByComparator((tab) => tab.node && tab.node.rid === this.rid);
this.container.tabsManager.closeTabsByComparator((tab) => tab.node && tab.node.rid === this.rid);
this.collection.children.remove(this);
},
(reason) => {}

View File

@ -44,7 +44,6 @@ export default class UserDefinedFunction {
collection: source,
node: source,
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(source.databaseId, source.id())}/udf`,
isActive: ko.observable(false),
onUpdateTabsButtons: source.container.onUpdateTabsButtons,
});
@ -82,7 +81,6 @@ export default class UserDefinedFunction {
this.collection.databaseId,
this.collection.id()
)}/udfs/${this.id()}`,
isActive: ko.observable(false),
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
});
@ -106,7 +104,7 @@ export default class UserDefinedFunction {
deleteUserDefinedFunction(this.collection.databaseId, this.collection.id(), this.id()).then(
() => {
this.container.tabsManager.removeTabByComparator((tab) => tab.node && tab.node.rid === this.rid);
this.container.tabsManager.closeTabsByComparator((tab) => tab.node && tab.node.rid === this.rid);
this.collection.children.remove(this);
},
(reason) => {}