Create tabs manager and refactor tab related logic (#66)

Co-authored-by: Steve Faulkner <southpolesteve@gmail.com>
This commit is contained in:
victor-meng
2020-07-09 13:53:37 -07:00
committed by GitHub
parent 326bd4f494
commit 4068a9fbaa
39 changed files with 3430 additions and 3139 deletions

View File

@@ -21,7 +21,7 @@ import DeleteIcon from "../../../images/delete.svg";
import { QueryIterator, ItemDefinition, Resource, ConflictDefinition } from "@azure/cosmos";
import { MinimalQueryIterator } from "../../Common/IteratorUtilities";
export class ConflictsTab extends TabsBase implements ViewModels.ConflictsTab {
export default class ConflictsTab extends TabsBase implements ViewModels.ConflictsTab {
public selectedConflictId: ko.Observable<ViewModels.ConflictId>;
public selectedConflictContent: ViewModels.Editable<string>;
public selectedConflictCurrent: ViewModels.Editable<string>;

View File

@@ -20,8 +20,7 @@ describe("Documents tab", () => {
hashLocation: "",
isActive: ko.observable<boolean>(false),
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
openedTabs: []
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
});
expect(documentsTab.buildQuery("")).toContain("select");
@@ -104,8 +103,7 @@ describe("Documents tab", () => {
hashLocation: "",
isActive: ko.observable<boolean>(false),
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
openedTabs: []
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
});
expect(documentsTab.showPartitionKey).toBe(false);
@@ -124,8 +122,7 @@ describe("Documents tab", () => {
hashLocation: "",
isActive: ko.observable<boolean>(false),
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
openedTabs: []
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
});
expect(documentsTab.showPartitionKey).toBe(false);
@@ -144,8 +141,7 @@ describe("Documents tab", () => {
hashLocation: "",
isActive: ko.observable<boolean>(false),
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
openedTabs: []
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
});
expect(documentsTab.showPartitionKey).toBe(true);
@@ -164,8 +160,7 @@ describe("Documents tab", () => {
hashLocation: "",
isActive: ko.observable<boolean>(false),
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
openedTabs: []
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
});
expect(documentsTab.showPartitionKey).toBe(false);
@@ -184,8 +179,7 @@ describe("Documents tab", () => {
hashLocation: "",
isActive: ko.observable<boolean>(false),
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
openedTabs: []
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
});
expect(documentsTab.showPartitionKey).toBe(true);

View File

@@ -28,8 +28,7 @@ describe("Query Tab", () => {
selfLink: "",
isActive: ko.observable<boolean>(false),
hashLocation: "",
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
openedTabs: []
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
});
}

View File

@@ -76,8 +76,7 @@ describe("Settings tab", () => {
quotaInfo,
null
),
onUpdateTabsButtons: undefined,
openedTabs: []
onUpdateTabsButtons: undefined
});
};
@@ -196,8 +195,7 @@ describe("Settings tab", () => {
hashLocation: "",
isActive: ko.observable(false),
collection: new Collection(explorer, "mydb", baseCollection, quotaInfo, null),
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
openedTabs: []
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
});
expect(settingsTab.shouldUpdateCollection()).toBe(false);
@@ -221,8 +219,7 @@ describe("Settings tab", () => {
hashLocation: "",
isActive: ko.observable(false),
collection: new Collection(explorer, "mydb", baseCollection, quotaInfo, null),
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
openedTabs: []
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
});
expect(settingsTab.shouldUpdateCollection()).toBe(false);
@@ -241,8 +238,7 @@ describe("Settings tab", () => {
hashLocation: "",
isActive: ko.observable(false),
collection: new Collection(explorer, "mydb", baseCollection, quotaInfo, null),
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
openedTabs: []
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
});
expect(settingsTab.shouldUpdateCollection()).toBe(false);
@@ -281,8 +277,7 @@ describe("Settings tab", () => {
hashLocation: "",
isActive: ko.observable(false),
collection: new Collection(explorer, "mydb", baseCollection, quotaInfo, null),
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
openedTabs: []
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
});
expect(settingsTab.getUpdatedConflictResolutionPolicy()).toBe(null);
@@ -299,8 +294,7 @@ describe("Settings tab", () => {
hashLocation: "",
isActive: ko.observable(false),
collection: new Collection(explorer, "mydb", baseCollection, quotaInfo, null),
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
openedTabs: []
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
});
expect(settingsTab.getUpdatedConflictResolutionPolicy()).toBe(null);
@@ -326,8 +320,7 @@ describe("Settings tab", () => {
hashLocation: "",
isActive: ko.observable(false),
collection: new Collection(explorer, "mydb", baseCollection, quotaInfo, null),
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
openedTabs: []
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
});
expect(settingsTab.getUpdatedConflictResolutionPolicy()).toBe(null);
@@ -408,8 +401,7 @@ describe("Settings tab", () => {
hashLocation: "",
isActive: ko.observable(false),
collection: getCollection(defaultApi, partitionKeyOption),
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
openedTabs: []
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
});
}
@@ -555,8 +547,7 @@ describe("Settings tab", () => {
hashLocation: "",
isActive: ko.observable(false),
collection: getCollection(autoPilotTier),
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
openedTabs: []
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
});
}
describe("Visible", () => {

View File

@@ -252,7 +252,7 @@ export default class SettingsTab extends TabsBase implements ViewModels.Settings
this.throughputModeRadioName = `throughputModeRadio${this.tabId}`;
this.changeFeedPolicyToggled = editable.observable<ChangeFeedPolicyToggledState>(
this.collection.rawDataModel.changeFeedPolicy != null
this.collection.rawDataModel?.changeFeedPolicy != null
? ChangeFeedPolicyToggledState.On
: ChangeFeedPolicyToggledState.Off
);

View File

@@ -16,6 +16,7 @@ import TriggerTabTemplate from "./TriggerTab.html";
import UserDefinedFunctionTabTemplate from "./UserDefinedFunctionTab.html";
import GalleryTabTemplate from "./GalleryTab.html";
import NotebookViewerTabTemplate from "./NotebookViewerTab.html";
import TabsManagerTemplate from "./TabsManager.html";
export class TabComponent {
constructor(data: any) {
@@ -23,6 +24,15 @@ export class TabComponent {
}
}
export class TabsManager {
constructor() {
return {
viewModel: TabComponent,
template: TabsManagerTemplate
};
}
}
export class DocumentsTab {
constructor() {
return {

View File

@@ -24,8 +24,6 @@ export default class TabsBase extends WaitsForTemplateViewModel implements ViewM
public tabKind: ViewModels.CollectionTabKind;
public tabTitle: ko.Observable<string>;
public tabPath: ko.Observable<string>;
public nextTab: ko.Observable<ViewModels.Tab>;
public previousTab: ko.Observable<ViewModels.Tab>;
public closeButtonTabIndex: ko.Computed<number>;
public errorDetailsTabIndex: ko.Computed<number>;
public hashLocation: ko.Observable<string>;
@@ -55,8 +53,6 @@ export default class TabsBase extends WaitsForTemplateViewModel implements ViewM
(options.tabPath && ko.observable<string>(options.tabPath)) ||
(this.collection &&
ko.observable<string>(`${this.collection.databaseId}>${this.collection.id()}>${this.tabTitle()}`));
this.nextTab = ko.observable<ViewModels.Tab>();
this.previousTab = ko.observable<ViewModels.Tab>();
this.closeButtonTabIndex = ko.computed<number>(() => (this.isActive() ? 0 : null));
this.errorDetailsTabIndex = ko.computed<number>(() => (this.isActive() ? 0 : null));
this.isExecutionError = ko.observable<boolean>(false);
@@ -80,34 +76,11 @@ export default class TabsBase extends WaitsForTemplateViewModel implements ViewM
return true;
})
};
const openedTabs = options.openedTabs;
if (openedTabs && openedTabs.length && openedTabs.length > 0) {
const lastTab = openedTabs[openedTabs.length - 1];
lastTab && lastTab.nextTab(this);
this.previousTab(lastTab);
}
}
public onCloseTabButtonClick(): Q.Promise<any> {
const previousTab = this.previousTab();
const nextTab = this.nextTab();
previousTab && previousTab.nextTab(nextTab);
nextTab && nextTab.previousTab(previousTab);
this.getContainer().openedTabs.remove(tab => tab.tabId === this.tabId);
const tabToActivate = nextTab || previousTab;
if (!tabToActivate) {
this.getContainer().selectedNode(null);
this.getContainer().onUpdateTabsButtons([]);
this.getContainer().activeTab(null);
} else {
tabToActivate.isActive(true);
this.getContainer().activeTab(tabToActivate);
}
public onCloseTabButtonClick(): void {
const explorer: ViewModels.Explorer = this.getContainer();
explorer.tabsManager.closeTab(this.tabId, explorer);
TelemetryProcessor.trace(Action.Tab, ActionModifiers.Close, {
databaseAccountName: this.getContainer().databaseAccount().name,
@@ -115,16 +88,10 @@ export default class TabsBase extends WaitsForTemplateViewModel implements ViewM
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle()
});
return Q();
}
public onTabClick(): Q.Promise<any> {
for (let i = 0; i < this.getContainer().openedTabs().length; i++) {
const tab = this.getContainer().openedTabs()[i];
tab.isActive(false);
}
this.isActive(true);
this.getContainer().activeTab(this);
this.getContainer().tabsManager.activateTab(this);
return Q();
}

View File

@@ -0,0 +1,148 @@
<div id="content" class="flexContainer hideOverflows" data-bind="visible: openedTabs && openedTabs().length > 0">
<!-- Tabs - Start -->
<div class="nav-tabs-margin">
<ul class="nav nav-tabs level navTabHeight" id="navTabs" role="tablist">
<!-- ko foreach: openedTabs -->
<li
class="tabList"
data-bind="
attr: {
title: $data.tabPath,
'aria-selected': $data.isActive,
'aria-expanded': $data.isActive,
'aria-controls': $data.tabId
},
css:{
active: $data.isActive
},
hasFocus: $data.hasFocus,
event: { keypress: onKeyPressActivate },
click: $data.onTabClick,"
tabindex="0"
role="tab"
>
<span class="tabNavContentContainer">
<a data-toggle="tab" data-bind="attr: { href: '#' + $data.tabId }" tabindex="-1">
<div class="tab_Content">
<span class="statusIconContainer">
<div
class="errorIconContainer"
id="errorStatusIcon"
title="Click to view more details"
role="button"
data-bind="
click: onErrorDetailsClick,
event: { keypress: onErrorDetailsKeyPress },
attr: { tabindex: errorDetailsTabIndex },
css: { actionsEnabled: isActive },
visible: isExecutionError"
>
<span class="errorIcon"></span>
</div>
<img
class="loadingIcon"
title="Loading"
src="/circular_loader_black_16x16.gif"
data-bind="visible: $data.isExecuting"
alt="Loading"
/>
</span>
<span class="tabNavText" data-bind="text: $data.tabTitle"></span>
<span class="tabIconSection">
<span
aria-label="Close Tab"
role="button"
class="cancelButton"
data-bind="
click: $data.onCloseTabButtonClick,
event: { keypress: onKeyPressClose },
attr: { tabindex: $data.closeButtonTabIndex },
visible: $data.isActive() || $data.isMouseOver()"
title="Close"
>
<span class="tabIcon close-Icon" data-bind="visible: $data.isActive() || $data.isMouseOver()">
<img src="../../../images/close-black.svg" title="Close" alt="Close" />
</span>
</span>
</span>
</div>
</a>
</span>
</li>
<!-- /ko -->
</ul>
</div>
<!-- Tabs -- End -->
<!-- Tabs Panes -- Start -->
<div class="tabPanesContainer">
<!-- ko foreach: openedTabs -->
<div class="tabs-container" data-bind="visible: $data.isActive">
<!-- ko if: $data.tabKind === 0 -->
<documents-tab params="{data: $data}"></documents-tab>
<!-- /ko -->
<!-- ko if: $data.tabKind === 1 -->
<settings-tab params="{data: $data}"></settings-tab>
<!-- /ko -->
<!-- ko if: $data.tabKind === 2 -->
<stored-procedure-tab params="{data: $data}"></stored-procedure-tab>
<!-- /ko -->
<!-- ko if: $data.tabKind === 3 -->
<user-defined-function-tab params="{data: $data}"></user-defined-function-tab>
<!-- /ko -->
<!-- ko if: $data.tabKind === 4 -->
<trigger-tab params="{data: $data}"></trigger-tab>
<!-- /ko -->
<!-- ko if: $data.tabKind === 5 -->
<query-tab params="{data: $data}"></query-tab>
<!-- /ko -->
<!-- ko if: $data.tabKind === 6 -->
<graph-tab params="{data: $data}"></graph-tab>
<!-- /ko -->
<!-- ko if: $data.tabKind === 9 -->
<tables-query-tab class="flexContainer" params="{data: $data}"></tables-query-tab>
<!-- /ko -->
<!-- ko if: $data.tabKind === 10 -->
<mongo-shell-tab params="{data: $data}"></mongo-shell-tab>
<!-- /ko -->
<!-- ko if: $data.tabKind === 11 -->
<database-settings-tab params="{data: $data}"></database-settings-tab>
<!-- /ko -->
<!-- ko if: $data.tabKind === 12 -->
<conflicts-tab params="{data: $data}"></conflicts-tab>
<!-- /ko -->
<!-- ko if: $data.tabKind === 14 -->
<terminal-tab params="{data: $data}"></terminal-tab>
<!-- /ko -->
<!-- ko if: $data.tabKind === 15 -->
<notebookv2-tab params="{data: $data}"></notebookv2-tab>
<!-- /ko -->
<!-- ko if: $data.tabKind === 16 -->
<spark-master-tab params="{data: $data}"></spark-master-tab>
<!-- /ko -->
<!-- ko if: $data.tabKind === 17 -->
<gallery-tab params="{data: $data}"></gallery-tab>
<!-- /ko -->
<!-- ko if: $data.tabKind === 18 -->
<notebook-viewer-tab params="{data: $data}"></notebook-viewer-tab>
<!-- /ko -->
</div>
<!-- /ko -->
</div>
<!-- Tabs Panes - End -->
</div>

View File

@@ -0,0 +1,138 @@
import * as ko from "knockout";
import * as ViewModels from "../../Contracts/ViewModels";
import { CollectionStub, DatabaseStub } from "../../Explorer/OpenActionsStubs";
import { DataAccessUtility } from "../../Platform/Portal/DataAccessUtility";
import { TabsManager } from "./TabsManager";
import DocumentClientUtilityBase from "../../Common/DocumentClientUtilityBase";
import DocumentsTab from "./DocumentsTab";
import Explorer from "../Explorer";
import QueryTab from "./QueryTab";
describe("Tabs manager tests", () => {
let tabsManager: TabsManager;
let explorer: ViewModels.Explorer;
let database: ViewModels.Database;
let collection: ViewModels.Collection;
let queryTab: QueryTab;
let documentsTab: DocumentsTab;
beforeAll(() => {
explorer = new Explorer({ documentClientUtility: undefined, notificationsClient: undefined, isEmulator: false });
explorer.databaseAccount = ko.observable<ViewModels.DatabaseAccount>({
id: "test",
name: "test",
location: "",
type: "",
kind: "",
tags: "",
properties: undefined
});
database = new DatabaseStub({
container: explorer,
id: ko.observable<string>("test"),
isDatabaseShared: () => false
});
database.isDatabaseExpanded = ko.observable<boolean>(true);
database.selectedSubnodeKind = ko.observable<ViewModels.CollectionTabKind>();
collection = new CollectionStub({
container: explorer,
databaseId: "test",
id: ko.observable<string>("test")
});
collection.getDatabase = (): ViewModels.Database => database;
collection.isCollectionExpanded = ko.observable<boolean>(true);
collection.selectedSubnodeKind = ko.observable<ViewModels.CollectionTabKind>();
queryTab = new QueryTab({
tabKind: ViewModels.CollectionTabKind.Query,
collection,
database,
title: "",
tabPath: "",
documentClientUtility: explorer.documentClientUtility,
selfLink: "",
isActive: ko.observable<boolean>(false),
hashLocation: "",
onUpdateTabsButtons: undefined
});
documentsTab = new DocumentsTab({
partitionKey: undefined,
documentIds: ko.observableArray<ViewModels.DocumentId>(),
tabKind: ViewModels.CollectionTabKind.Documents,
collection,
title: "",
tabPath: "",
documentClientUtility: new DocumentClientUtilityBase(new DataAccessUtility()),
selfLink: "",
hashLocation: "",
isActive: ko.observable<boolean>(false),
onUpdateTabsButtons: undefined
});
});
beforeEach(() => {
tabsManager = new TabsManager();
explorer.tabsManager = tabsManager;
});
it("open new tabs", () => {
tabsManager.activateNewTab(queryTab);
expect(tabsManager.openedTabs().length).toBe(1);
expect(tabsManager.openedTabs()[0]).toEqual(queryTab);
expect(tabsManager.activeTab()).toEqual(queryTab);
expect(queryTab.isActive()).toBe(true);
tabsManager.activateNewTab(documentsTab);
expect(tabsManager.openedTabs().length).toBe(2);
expect(tabsManager.openedTabs()[1]).toEqual(documentsTab);
expect(tabsManager.activeTab()).toEqual(documentsTab);
expect(queryTab.isActive()).toBe(false);
expect(documentsTab.isActive()).toBe(true);
});
it("open existing tabs", () => {
tabsManager.activateNewTab(queryTab);
tabsManager.activateNewTab(documentsTab);
tabsManager.activateTab(queryTab);
expect(tabsManager.openedTabs().length).toBe(2);
expect(tabsManager.activeTab()).toEqual(queryTab);
expect(queryTab.isActive()).toBe(true);
expect(documentsTab.isActive()).toBe(false);
});
it("get tabs", () => {
tabsManager.activateNewTab(queryTab);
tabsManager.activateNewTab(documentsTab);
const queryTabs: ViewModels.Tab[] = tabsManager.getTabs(ViewModels.CollectionTabKind.Query);
expect(queryTabs.length).toBe(1);
expect(queryTabs[0]).toEqual(queryTab);
const documentsTabs: ViewModels.Tab[] = tabsManager.getTabs(
ViewModels.CollectionTabKind.Documents,
(tab: ViewModels.Tab) => tab.tabId === documentsTab.tabId
);
expect(documentsTabs.length).toBe(1);
expect(documentsTabs[0]).toEqual(documentsTab);
});
it("close tabs", () => {
tabsManager.activateNewTab(queryTab);
tabsManager.activateNewTab(documentsTab);
tabsManager.closeTab(documentsTab.tabId, explorer);
expect(tabsManager.openedTabs().length).toBe(1);
expect(tabsManager.openedTabs()[0]).toEqual(queryTab);
expect(tabsManager.activeTab()).toEqual(queryTab);
expect(queryTab.isActive()).toBe(true);
expect(documentsTab.isActive()).toBe(false);
tabsManager.closeTabsByComparator((tab: ViewModels.Tab) => tab.tabId === queryTab.tabId);
expect(tabsManager.openedTabs().length).toBe(0);
expect(tabsManager.activeTab()).toEqual(undefined);
expect(queryTab.isActive()).toBe(false);
});
});

View File

@@ -0,0 +1,96 @@
import * as ko from "knockout";
import * as ViewModels from "../../Contracts/ViewModels";
import TabsManagerTemplate from "./TabsManager.html";
export class TabsManager {
public openedTabs: ko.ObservableArray<ViewModels.Tab>;
public activeTab: ko.Observable<ViewModels.Tab>;
constructor() {
this.openedTabs = ko.observableArray<ViewModels.Tab>([]);
this.activeTab = ko.observable<ViewModels.Tab>();
}
public activateNewTab(tab: ViewModels.Tab): void {
this.openedTabs.push(tab);
this.activateTab(tab);
}
public activateTab(tab: ViewModels.Tab): void {
this.activeTab() && this.activeTab().isActive(false);
tab.isActive(true);
this.activeTab(tab);
}
public getTabs(
tabKind: ViewModels.CollectionTabKind,
comparator?: (tab: ViewModels.Tab) => boolean
): ViewModels.Tab[] {
return this.openedTabs().filter((openedTab: ViewModels.Tab) => {
return openedTab.tabKind === tabKind && (!comparator || comparator(openedTab));
});
}
public refreshActiveTab(comparator: (tab: ViewModels.Tab) => boolean): void {
// ensures that the tab selects/highlights the right node based on resource tree expand/collapse state
this.openedTabs().forEach((tab: ViewModels.Tab) => {
if (comparator(tab) && tab.isActive()) {
tab.onActivate();
}
});
}
public removeTabById(tabId: string): void {
this.openedTabs.remove((tab: ViewModels.Tab) => tab.tabId === tabId);
}
public removeTabByComparator(comparator: (tab: ViewModels.Tab) => boolean): void {
this.openedTabs.remove((tab: ViewModels.Tab) => comparator(tab));
}
public closeTabsByComparator(comparator: (tab: ViewModels.Tab) => boolean): void {
this.activeTab() && this.activeTab().isActive(false);
this.activeTab(undefined);
this.openedTabs().forEach((tab: ViewModels.Tab) => {
if (comparator(tab)) {
tab.onCloseTabButtonClick();
}
});
}
public closeTabs(): void {
this.openedTabs([]);
}
public closeTab(tabId: string, explorer: ViewModels.Explorer): void {
const tabIndex: number = this.openedTabs().findIndex((tab: ViewModels.Tab) => tab.tabId === tabId);
if (tabIndex !== -1) {
const tabToActive: ViewModels.Tab = 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.activeTab(undefined);
}
}
}
public isTabActive(tabKind: ViewModels.CollectionTabKind): boolean {
return this.activeTab() && this.activeTab().tabKind === tabKind;
}
}
function TabsManagerWrapperViewModel(params: { data: TabsManager }) {
return params.data;
}
export function TabsManagerKOComponent(): unknown {
return {
viewModel: TabsManagerWrapperViewModel,
template: TabsManagerTemplate
};
}