Create tabs manager and refactor tab related logic (#66)
Co-authored-by: Steve Faulkner <southpolesteve@gmail.com>
This commit is contained in:
parent
326bd4f494
commit
4068a9fbaa
File diff suppressed because it is too large
Load Diff
|
@ -2349,6 +2349,11 @@ a:link {
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
.tabsContainer {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
position: relative;
|
||||
margin: 15px 0 25px 0;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -22,6 +22,7 @@ import { QueryMetrics } from "@azure/cosmos";
|
|||
import { SetupNotebooksPane } from "../Explorer/Panes/SetupNotebooksPane";
|
||||
import { Splitter } from "../Common/Splitter";
|
||||
import { StringInputPane } from "../Explorer/Panes/StringInputPane";
|
||||
import { TabsManager } from "../Explorer/Tabs/TabsManager";
|
||||
import { TextFieldProps } from "../Explorer/Controls/DialogReactComponent/DialogComponent";
|
||||
import { UploadDetails } from "../workers/upload/definitions";
|
||||
import { UploadItemsPaneAdapter } from "../Explorer/Panes/UploadItemsPaneAdapter";
|
||||
|
@ -121,9 +122,8 @@ export interface Explorer {
|
|||
isResourceTokenCollectionNodeSelected: ko.Computed<boolean>;
|
||||
|
||||
// Tabs
|
||||
openedTabs: ko.ObservableArray<Tab>;
|
||||
activeTab: ko.Observable<Tab>;
|
||||
isTabsContentExpanded: ko.Observable<boolean>;
|
||||
tabsManager: TabsManager;
|
||||
|
||||
// Contextual Panes
|
||||
addDatabasePane: AddDatabasePane;
|
||||
|
@ -161,8 +161,6 @@ export interface Explorer {
|
|||
refreshDatabaseForResourceToken(): Q.Promise<void>;
|
||||
refreshAllDatabases(isInitialLoad?: boolean): Q.Promise<any>;
|
||||
closeAllPanes(): void;
|
||||
closeAllTabsForResource(resourceId: string): void;
|
||||
findActiveTab(): Tab; // TODO Deprecate in favor activeTab
|
||||
findSelectedDatabase(): Database;
|
||||
findDatabaseWithId(databaseRid: string): Database;
|
||||
isLastDatabase(): boolean;
|
||||
|
@ -221,7 +219,6 @@ export interface Explorer {
|
|||
sparkClusterConnectionInfo: ko.Observable<DataModels.SparkClusterConnectionInfo>;
|
||||
arcadiaToken: ko.Observable<string>;
|
||||
arcadiaWorkspaces: ko.ObservableArray<ArcadiaWorkspaceItem>;
|
||||
isNotebookTabActive: ko.Computed<boolean>;
|
||||
memoryUsageInfo: ko.Observable<DataModels.MemoryUsageInfo>;
|
||||
notebookManager?: any; // This is dynamically loaded
|
||||
openNotebook(notebookContentItem: NotebookContentItem): Promise<boolean>; // True if it was opened, false otherwise
|
||||
|
@ -250,7 +247,6 @@ export interface Explorer {
|
|||
readFile: (notebookFile: NotebookContentItem) => Promise<string>;
|
||||
downloadFile: (notebookFile: NotebookContentItem) => Promise<void>;
|
||||
createNotebookContentItemFile: (name: string, filepath: string) => NotebookContentItem;
|
||||
closeNotebookTab: (filepath: string) => void;
|
||||
refreshContentItem(item: NotebookContentItem): Promise<void>;
|
||||
getNotebookBasePath(): string;
|
||||
|
||||
|
@ -381,7 +377,6 @@ export interface Database extends TreeNode {
|
|||
findCollectionWithId(collectionRid: string): Collection;
|
||||
openAddCollection(database: Database, event: MouseEvent): void;
|
||||
onDeleteDatabaseContextMenuClick(source: Database, event: MouseEvent | KeyboardEvent): void;
|
||||
refreshTabSelectedState(): void;
|
||||
readSettings(): void;
|
||||
onSettingsClick: () => void;
|
||||
}
|
||||
|
@ -403,7 +398,6 @@ export interface CollectionBase extends TreeNode {
|
|||
onNewQueryClick(source: any, event: MouseEvent, queryText?: string): void;
|
||||
expandCollection(): Q.Promise<any>;
|
||||
collapseCollection(): void;
|
||||
refreshActiveTab(): void;
|
||||
getDatabase(): Database;
|
||||
}
|
||||
|
||||
|
@ -837,7 +831,6 @@ export interface TabOptions {
|
|||
onUpdateTabsButtons: (buttons: NavbarButtonConfig[]) => void;
|
||||
isTabsContentExpanded?: ko.Observable<boolean>;
|
||||
onLoadStartKey?: number;
|
||||
openedTabs: Tab[];
|
||||
|
||||
// TODO Remove the flag and use a context to handle this
|
||||
// TODO: 145357 Remove dependency on collection/database and add abstraction
|
||||
|
@ -931,14 +924,12 @@ export interface Tab {
|
|||
tabTitle: ko.Observable<string>;
|
||||
hashLocation: ko.Observable<string>;
|
||||
closeTabButton: Button;
|
||||
onCloseTabButtonClick(): Q.Promise<any>;
|
||||
onCloseTabButtonClick(): void;
|
||||
onTabClick(): Q.Promise<any>;
|
||||
onKeyPressActivate(source: any, event: KeyboardEvent): void;
|
||||
onKeyPressClose(source: any, event: KeyboardEvent): void;
|
||||
onActivate(): Q.Promise<any>;
|
||||
refresh(): void;
|
||||
nextTab: ko.Observable<Tab>;
|
||||
previousTab: ko.Observable<Tab>;
|
||||
closeButtonTabIndex: ko.Computed<number>;
|
||||
isExecutionError: ko.Observable<boolean>;
|
||||
isExecuting: ko.Observable<boolean>;
|
||||
|
|
|
@ -10,6 +10,7 @@ import { GraphStyleComponent } from "./Graph/GraphStyleComponent/GraphStyleCompo
|
|||
import { InputTypeaheadComponent } from "./Controls/InputTypeahead/InputTypeahead";
|
||||
import { JsonEditorComponent } from "./Controls/JsonEditor/JsonEditorComponent";
|
||||
import { NewVertexComponent } from "./Graph/NewVertexComponent/NewVertexComponent";
|
||||
import { TabsManagerKOComponent } from "./Tabs/TabsManager";
|
||||
import { ThroughputInputComponent } from "./Controls/ThroughputInput/ThroughputInputComponent";
|
||||
import { ThroughputInputComponentAutoPilotV3 } from "./Controls/ThroughputInput/ThroughputInputComponentAutoPilotV3";
|
||||
import { ToolbarComponent } from "./Controls/Toolbar/Toolbar";
|
||||
|
@ -26,6 +27,7 @@ ko.components.register("diff-editor", new DiffEditorComponent());
|
|||
ko.components.register("dynamic-list", DynamicListComponent);
|
||||
ko.components.register("throughput-input", ThroughputInputComponent);
|
||||
ko.components.register("throughput-input-autopilot-v3", ThroughputInputComponentAutoPilotV3);
|
||||
ko.components.register("tabs-manager", TabsManagerKOComponent());
|
||||
|
||||
// Collection Tabs
|
||||
ko.components.register("documents-tab", new TabComponents.DocumentsTab());
|
||||
|
|
|
@ -77,6 +77,7 @@ import { SplashScreenComponentAdapter } from "./SplashScreen/SplashScreenCompone
|
|||
import { Splitter, SplitterBounds, SplitterDirection } from "../Common/Splitter";
|
||||
import { StringInputPane } from "./Panes/StringInputPane";
|
||||
import { TableColumnOptionsPane } from "./Panes/Tables/TableColumnOptionsPane";
|
||||
import { TabsManager } from "./Tabs/TabsManager";
|
||||
import { UploadFilePane } from "./Panes/UploadFilePane";
|
||||
import { UploadItemsPane } from "./Panes/UploadItemsPane";
|
||||
import { UploadItemsPaneAdapter } from "./Panes/UploadItemsPaneAdapter";
|
||||
|
@ -161,11 +162,10 @@ export default class Explorer implements ViewModels.Explorer {
|
|||
private resourceTreeForResourceToken: ResourceTreeAdapterForResourceToken;
|
||||
|
||||
// Tabs
|
||||
public openedTabs: ko.ObservableArray<ViewModels.Tab>;
|
||||
public activeTab: ko.Observable<ViewModels.Tab>;
|
||||
public isTabsContentExpanded: ko.Observable<boolean>;
|
||||
public galleryTab: any;
|
||||
public notebookViewerTab: any;
|
||||
public tabsManager: TabsManager;
|
||||
|
||||
// Contextual panes
|
||||
public addDatabasePane: ViewModels.AddDatabasePane;
|
||||
|
@ -229,7 +229,6 @@ export default class Explorer implements ViewModels.Explorer {
|
|||
public arcadiaWorkspaces: ko.ObservableArray<ArcadiaWorkspaceItem>;
|
||||
public hasStorageAnalyticsAfecFeature: ko.Observable<boolean>;
|
||||
public isSynapseLinkUpdating: ko.Observable<boolean>;
|
||||
public isNotebookTabActive: ko.Computed<boolean>;
|
||||
public memoryUsageInfo: ko.Observable<DataModels.MemoryUsageInfo>;
|
||||
public notebookManager?: any; // This is dynamically loaded
|
||||
|
||||
|
@ -296,14 +295,10 @@ export default class Explorer implements ViewModels.Explorer {
|
|||
this.arcadiaToken = ko.observable<string>();
|
||||
this.arcadiaToken.subscribe((token: string) => {
|
||||
if (token) {
|
||||
this.openedTabs &&
|
||||
this.openedTabs().forEach(tab => {
|
||||
if (tab.tabKind === ViewModels.CollectionTabKind.Notebook) {
|
||||
throw new Error("NotebookTab is deprecated. Use NotebookV2Tab");
|
||||
} else if (tab.tabKind === ViewModels.CollectionTabKind.NotebookV2) {
|
||||
(tab as NotebookV2Tab).reconfigureServiceEndpoints();
|
||||
}
|
||||
});
|
||||
const notebookTabs = this.tabsManager.getTabs(ViewModels.CollectionTabKind.NotebookV2);
|
||||
(notebookTabs || []).forEach((tab: NotebookV2Tab) => {
|
||||
tab.reconfigureServiceEndpoints();
|
||||
});
|
||||
}
|
||||
});
|
||||
this.isNotebooksEnabledForAccount = ko.observable(false);
|
||||
|
@ -771,12 +766,8 @@ export default class Explorer implements ViewModels.Explorer {
|
|||
container: this
|
||||
});
|
||||
|
||||
this.openedTabs = ko.observableArray<ViewModels.Tab>([]);
|
||||
this.activeTab = ko.observable(null);
|
||||
this.isNotebookTabActive = ko.computed<boolean>(() => {
|
||||
// only show memory tracker if the current active tab is a notebook
|
||||
return this.activeTab() && this.activeTab().tabKind === ViewModels.CollectionTabKind.NotebookV2;
|
||||
});
|
||||
this.tabsManager = new TabsManager();
|
||||
|
||||
this._panes = [
|
||||
this.addDatabasePane,
|
||||
this.addCollectionPane,
|
||||
|
@ -1262,7 +1253,7 @@ export default class Explorer implements ViewModels.Explorer {
|
|||
class: "connectDialogButtons okBtn connectOkBtns",
|
||||
click: () => {
|
||||
$("#contextSwitchPrompt").dialog("close");
|
||||
this.openedTabs([]); // clear all tabs so we dont leave any tabs from previous session open
|
||||
this.tabsManager.closeTabs(); // clear all tabs so we dont leave any tabs from previous session open
|
||||
this.renewShareAccess(connectionString);
|
||||
}
|
||||
};
|
||||
|
@ -2090,10 +2081,6 @@ export default class Explorer implements ViewModels.Explorer {
|
|||
return Q();
|
||||
}
|
||||
|
||||
public findActiveTab(): ViewModels.Tab {
|
||||
return this.activeTab();
|
||||
}
|
||||
|
||||
public findSelectedCollection(): ViewModels.Collection {
|
||||
if (this.selectedNode().nodeKind === "Collection") {
|
||||
return this.findSelectedCollectionForSelectedNode();
|
||||
|
@ -2106,11 +2093,9 @@ export default class Explorer implements ViewModels.Explorer {
|
|||
public findSelectedStoredProcedure(): ViewModels.StoredProcedure {
|
||||
const selectedCollection: ViewModels.Collection = this.findSelectedCollection();
|
||||
return _.find(selectedCollection.storedProcedures(), (storedProcedure: ViewModels.StoredProcedure) => {
|
||||
const openedSprocTab = this.openedTabs().filter(
|
||||
(tab: ViewModels.Tab) =>
|
||||
tab.node &&
|
||||
tab.node.rid === storedProcedure.rid &&
|
||||
tab.tabKind === ViewModels.CollectionTabKind.StoredProcedures
|
||||
const openedSprocTab = this.tabsManager.getTabs(
|
||||
ViewModels.CollectionTabKind.StoredProcedures,
|
||||
(tab: ViewModels.Tab) => tab.node && tab.node.rid === storedProcedure.rid
|
||||
);
|
||||
return (
|
||||
storedProcedure.rid === this.selectedNode().rid ||
|
||||
|
@ -2122,11 +2107,9 @@ export default class Explorer implements ViewModels.Explorer {
|
|||
public findSelectedUDF(): ViewModels.UserDefinedFunction {
|
||||
const selectedCollection: ViewModels.Collection = this.findSelectedCollection();
|
||||
return _.find(selectedCollection.userDefinedFunctions(), (userDefinedFunction: ViewModels.UserDefinedFunction) => {
|
||||
const openedUdfTab = this.openedTabs().filter(
|
||||
(tab: ViewModels.Tab) =>
|
||||
tab.node &&
|
||||
tab.node.rid === userDefinedFunction.rid &&
|
||||
tab.tabKind === ViewModels.CollectionTabKind.UserDefinedFunctions
|
||||
const openedUdfTab = this.tabsManager.getTabs(
|
||||
ViewModels.CollectionTabKind.UserDefinedFunctions,
|
||||
(tab: ViewModels.Tab) => tab.node && tab.node.rid === userDefinedFunction.rid
|
||||
);
|
||||
return (
|
||||
userDefinedFunction.rid === this.selectedNode().rid ||
|
||||
|
@ -2138,9 +2121,9 @@ export default class Explorer implements ViewModels.Explorer {
|
|||
public findSelectedTrigger(): ViewModels.Trigger {
|
||||
const selectedCollection: ViewModels.Collection = this.findSelectedCollection();
|
||||
return _.find(selectedCollection.triggers(), (trigger: ViewModels.Trigger) => {
|
||||
const openedTriggerTab = this.openedTabs().filter(
|
||||
(tab: ViewModels.Tab) =>
|
||||
tab.node && tab.node.rid === trigger.rid && tab.tabKind === ViewModels.CollectionTabKind.Triggers
|
||||
const openedTriggerTab = this.tabsManager.getTabs(
|
||||
ViewModels.CollectionTabKind.Triggers,
|
||||
(tab: ViewModels.Tab) => tab.node && tab.node.rid === trigger.rid
|
||||
);
|
||||
return (
|
||||
trigger.rid === this.selectedNode().rid ||
|
||||
|
@ -2149,16 +2132,6 @@ export default class Explorer implements ViewModels.Explorer {
|
|||
});
|
||||
}
|
||||
|
||||
public closeAllTabsForResource(resourceId: string): void {
|
||||
const currentlyActiveTabs = this.openedTabs().filter((tab: ViewModels.Tab) => tab.isActive && tab.isActive());
|
||||
currentlyActiveTabs.forEach((tab: ViewModels.Tab) => tab.isActive(false));
|
||||
this.activeTab(null);
|
||||
const openedTabsForResource = this.openedTabs().filter(
|
||||
(tab: ViewModels.Tab) => tab.node && tab.node.rid === resourceId
|
||||
);
|
||||
openedTabsForResource.forEach((tab: ViewModels.Tab) => tab.onCloseTabButtonClick());
|
||||
}
|
||||
|
||||
public closeAllPanes(): void {
|
||||
this._panes.forEach((pane: ViewModels.ContextualPane) => pane.close());
|
||||
}
|
||||
|
@ -2235,7 +2208,9 @@ export default class Explorer implements ViewModels.Explorer {
|
|||
if (isNewDatabase) {
|
||||
database.expandDatabase();
|
||||
}
|
||||
database.refreshTabSelectedState();
|
||||
this.tabsManager.refreshActiveTab(
|
||||
(tab: ViewModels.Tab) => tab.collection && tab.collection.getDatabase().rid === database.rid
|
||||
);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
@ -2338,7 +2313,7 @@ export default class Explorer implements ViewModels.Explorer {
|
|||
}
|
||||
|
||||
const urlPrefixWithKeyParam: string = `${config.hostedExplorerURL}?key=`;
|
||||
const currentActiveTab: ViewModels.Tab = this.findActiveTab();
|
||||
const currentActiveTab: ViewModels.Tab = this.tabsManager.activeTab();
|
||||
|
||||
return `${urlPrefixWithKeyParam}${token}#/${(currentActiveTab && currentActiveTab.hashLocation()) || ""}`;
|
||||
}
|
||||
|
@ -2600,46 +2575,48 @@ export default class Explorer implements ViewModels.Explorer {
|
|||
if (!notebookContentItem || !notebookContentItem.path) {
|
||||
throw new Error(`Invalid notebookContentItem: ${notebookContentItem}`);
|
||||
}
|
||||
const openedNotebookTabs = this.getNotebookTabsForFilepath(notebookContentItem.path);
|
||||
|
||||
if (openedNotebookTabs.length > 0) {
|
||||
openedNotebookTabs[0].onTabClick();
|
||||
openedNotebookTabs[0].onActivate();
|
||||
return true;
|
||||
const notebookTabs: NotebookV2Tab[] = this.tabsManager.getTabs(
|
||||
ViewModels.CollectionTabKind.NotebookV2,
|
||||
(tab: ViewModels.Tab) =>
|
||||
(tab as NotebookV2Tab).notebookPath &&
|
||||
FileSystemUtil.isPathEqual((tab as NotebookV2Tab).notebookPath(), notebookContentItem.path)
|
||||
) as NotebookV2Tab[];
|
||||
let notebookTab: NotebookV2Tab = notebookTabs && notebookTabs[0];
|
||||
|
||||
if (notebookTab) {
|
||||
this.tabsManager.activateTab(notebookTab);
|
||||
} else {
|
||||
const options: ViewModels.NotebookTabOptions = {
|
||||
account: CosmosClient.databaseAccount(),
|
||||
tabKind: ViewModels.CollectionTabKind.NotebookV2,
|
||||
node: null,
|
||||
title: notebookContentItem.name,
|
||||
tabPath: notebookContentItem.path,
|
||||
documentClientUtility: null,
|
||||
|
||||
collection: null,
|
||||
selfLink: null,
|
||||
masterKey: CosmosClient.masterKey() || "",
|
||||
hashLocation: "notebooks",
|
||||
isActive: ko.observable(false),
|
||||
isTabsContentExpanded: ko.observable(true),
|
||||
onLoadStartKey: null,
|
||||
onUpdateTabsButtons: this.onUpdateTabsButtons,
|
||||
container: this,
|
||||
notebookContentItem
|
||||
};
|
||||
|
||||
try {
|
||||
const NotebookTabV2 = await import(/* webpackChunkName: "NotebookV2Tab" */ "./Tabs/NotebookV2Tab");
|
||||
notebookTab = new NotebookTabV2.default(options);
|
||||
this.tabsManager.activateNewTab(notebookTab);
|
||||
} catch (reason) {
|
||||
console.error("Import NotebookV2Tab failed!", reason);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const options: ViewModels.NotebookTabOptions = {
|
||||
account: CosmosClient.databaseAccount(),
|
||||
tabKind: ViewModels.CollectionTabKind.NotebookV2,
|
||||
node: null,
|
||||
title: notebookContentItem.name,
|
||||
tabPath: notebookContentItem.path,
|
||||
documentClientUtility: null,
|
||||
|
||||
collection: null,
|
||||
selfLink: null,
|
||||
masterKey: CosmosClient.masterKey() || "",
|
||||
hashLocation: "notebooks",
|
||||
isActive: ko.observable(false),
|
||||
isTabsContentExpanded: ko.observable(true),
|
||||
onLoadStartKey: null,
|
||||
onUpdateTabsButtons: this.onUpdateTabsButtons,
|
||||
container: this,
|
||||
notebookContentItem,
|
||||
openedTabs: this.openedTabs()
|
||||
};
|
||||
|
||||
try {
|
||||
const NotebookTabV2 = await import(/* webpackChunkName: "NotebookV2Tab" */ "./Tabs/NotebookV2Tab");
|
||||
const notebookTab = new NotebookTabV2.default(options);
|
||||
this.openedTabs.push(notebookTab);
|
||||
|
||||
// Activate
|
||||
notebookTab.onTabClick();
|
||||
} catch (reason) {
|
||||
console.error("Import NotebookV2Tab failed!", reason);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2652,10 +2629,11 @@ export default class Explorer implements ViewModels.Explorer {
|
|||
}
|
||||
|
||||
// Don't delete if tab is open to avoid accidental deletion
|
||||
const openedNotebookTabs = this.openedTabs().filter(
|
||||
(tab: ViewModels.Tab) =>
|
||||
tab.tabKind === ViewModels.CollectionTabKind.NotebookV2 &&
|
||||
(tab as NotebookV2Tab).notebookPath() === notebookFile.path
|
||||
const openedNotebookTabs = this.tabsManager.getTabs(
|
||||
ViewModels.CollectionTabKind.NotebookV2,
|
||||
(tab: NotebookV2Tab) => {
|
||||
return tab.notebookPath && FileSystemUtil.isPathEqual(tab.notebookPath(), notebookFile.path);
|
||||
}
|
||||
);
|
||||
if (openedNotebookTabs.length > 0) {
|
||||
this.showOkModalDialog("Unable to rename file", "This file is being edited. Please close the tab and try again.");
|
||||
|
@ -2675,17 +2653,15 @@ export default class Explorer implements ViewModels.Explorer {
|
|||
onSubmit: (input: string) => this.notebookManager?.notebookContentClient.renameNotebook(notebookFile, input)
|
||||
})
|
||||
.then(newNotebookFile => {
|
||||
this.openedTabs()
|
||||
.filter(
|
||||
(tab: ViewModels.Tab) =>
|
||||
tab.tabKind === ViewModels.CollectionTabKind.NotebookV2 &&
|
||||
FileSystemUtil.isPathEqual((tab as NotebookV2Tab).notebookPath(), originalPath)
|
||||
)
|
||||
.forEach(tab => {
|
||||
tab.tabTitle(newNotebookFile.name);
|
||||
tab.tabPath(newNotebookFile.path);
|
||||
(tab as NotebookV2Tab).notebookPath(newNotebookFile.path);
|
||||
});
|
||||
const notebookTabs: ViewModels.Tab[] = this.tabsManager.getTabs(
|
||||
ViewModels.CollectionTabKind.NotebookV2,
|
||||
(tab: NotebookV2Tab) => tab.notebookPath && FileSystemUtil.isPathEqual(tab.notebookPath(), originalPath)
|
||||
);
|
||||
notebookTabs.forEach(tab => {
|
||||
tab.tabTitle(newNotebookFile.name);
|
||||
tab.tabPath(newNotebookFile.path);
|
||||
(tab as NotebookV2Tab).notebookPath(newNotebookFile.path);
|
||||
});
|
||||
|
||||
return newNotebookFile;
|
||||
});
|
||||
|
@ -2865,39 +2841,34 @@ export default class Explorer implements ViewModels.Explorer {
|
|||
await this.initSparkConnectionInfo(this.databaseAccount());
|
||||
}
|
||||
|
||||
const openedSparkMasterTabs = this.openedTabs().filter(
|
||||
(tab: ViewModels.Tab) => tab.tabKind === ViewModels.CollectionTabKind.SparkMasterTab
|
||||
);
|
||||
if (openedSparkMasterTabs.length > 0) {
|
||||
openedSparkMasterTabs[0].onTabClick();
|
||||
openedSparkMasterTabs[0].onActivate();
|
||||
return;
|
||||
const sparkMasterTabs: SparkMasterTab[] = this.tabsManager.getTabs(
|
||||
ViewModels.CollectionTabKind.SparkMasterTab
|
||||
) as SparkMasterTab[];
|
||||
let sparkMasterTab: SparkMasterTab = sparkMasterTabs && sparkMasterTabs[0];
|
||||
|
||||
if (sparkMasterTab) {
|
||||
this.tabsManager.activateTab(sparkMasterTab);
|
||||
} else {
|
||||
sparkMasterTab = new SparkMasterTab({
|
||||
clusterConnectionInfo: this.sparkClusterConnectionInfo(),
|
||||
tabKind: ViewModels.CollectionTabKind.SparkMasterTab,
|
||||
node: null,
|
||||
title: "Apache Spark",
|
||||
tabPath: "",
|
||||
documentClientUtility: null,
|
||||
|
||||
collection: null,
|
||||
selfLink: null,
|
||||
hashLocation: "sparkmaster",
|
||||
isActive: ko.observable(false),
|
||||
isTabsContentExpanded: ko.observable(true),
|
||||
onLoadStartKey: null,
|
||||
onUpdateTabsButtons: this.onUpdateTabsButtons,
|
||||
container: this
|
||||
});
|
||||
|
||||
this.tabsManager.activateNewTab(sparkMasterTab);
|
||||
}
|
||||
|
||||
const sparkMasterTab = new SparkMasterTab({
|
||||
clusterConnectionInfo: this.sparkClusterConnectionInfo(),
|
||||
tabKind: ViewModels.CollectionTabKind.SparkMasterTab,
|
||||
node: null,
|
||||
title: "Apache Spark",
|
||||
tabPath: "",
|
||||
documentClientUtility: null,
|
||||
|
||||
collection: null,
|
||||
selfLink: null,
|
||||
hashLocation: "sparkmaster",
|
||||
isActive: ko.observable(false),
|
||||
isTabsContentExpanded: ko.observable(true),
|
||||
onLoadStartKey: null,
|
||||
onUpdateTabsButtons: this.onUpdateTabsButtons,
|
||||
openedTabs: this.openedTabs(),
|
||||
container: this
|
||||
});
|
||||
|
||||
this.openedTabs.push(sparkMasterTab);
|
||||
|
||||
// Activate
|
||||
sparkMasterTab.onTabClick();
|
||||
return;
|
||||
}
|
||||
|
||||
private refreshNotebookList = async (): Promise<void> => {
|
||||
|
@ -2920,9 +2891,11 @@ export default class Explorer implements ViewModels.Explorer {
|
|||
}
|
||||
|
||||
// Don't delete if tab is open to avoid accidental deletion
|
||||
const openedNotebookTabs = this.openedTabs().filter(
|
||||
(tab: ViewModels.Tab) =>
|
||||
tab.tabKind === ViewModels.CollectionTabKind.NotebookV2 && (tab as NotebookV2Tab).notebookPath() === item.path
|
||||
const openedNotebookTabs = this.tabsManager.getTabs(
|
||||
ViewModels.CollectionTabKind.NotebookV2,
|
||||
(tab: NotebookV2Tab) => {
|
||||
return tab.notebookPath && FileSystemUtil.isPathEqual(tab.notebookPath(), item.path);
|
||||
}
|
||||
);
|
||||
if (openedNotebookTabs.length > 0) {
|
||||
this.showOkModalDialog("Unable to delete file", "This file is being edited. Please close the tab and try again.");
|
||||
|
@ -3084,94 +3057,79 @@ export default class Explorer implements ViewModels.Explorer {
|
|||
throw new Error("Terminal kind: ${kind} not supported");
|
||||
}
|
||||
|
||||
const openedTabs = this.openedTabs().filter(
|
||||
(tab: ViewModels.Tab) => tab.tabKind === ViewModels.CollectionTabKind.Terminal
|
||||
);
|
||||
const terminalTabs: TerminalTab[] = this.tabsManager.getTabs(
|
||||
ViewModels.CollectionTabKind.Terminal,
|
||||
(tab: ViewModels.Tab) => tab.hashLocation() == hashLocation
|
||||
) as TerminalTab[];
|
||||
let terminalTab: TerminalTab = terminalTabs && terminalTabs[0];
|
||||
|
||||
for (let i = 0; i < openedTabs.length; ++i) {
|
||||
if (openedTabs[i].hashLocation() == hashLocation) {
|
||||
openedTabs[i].onTabClick();
|
||||
openedTabs[i].onActivate();
|
||||
return;
|
||||
}
|
||||
if (terminalTab) {
|
||||
this.tabsManager.activateTab(terminalTab);
|
||||
} else {
|
||||
const newTab = new TerminalTab({
|
||||
account: CosmosClient.databaseAccount(),
|
||||
tabKind: ViewModels.CollectionTabKind.Terminal,
|
||||
node: null,
|
||||
title: title,
|
||||
tabPath: title,
|
||||
documentClientUtility: null,
|
||||
|
||||
collection: null,
|
||||
selfLink: null,
|
||||
hashLocation: hashLocation,
|
||||
isActive: ko.observable(false),
|
||||
isTabsContentExpanded: ko.observable(true),
|
||||
onLoadStartKey: null,
|
||||
onUpdateTabsButtons: this.onUpdateTabsButtons,
|
||||
container: this,
|
||||
kind: kind
|
||||
});
|
||||
|
||||
this.tabsManager.activateNewTab(newTab);
|
||||
}
|
||||
|
||||
const newTab = new TerminalTab({
|
||||
account: CosmosClient.databaseAccount(),
|
||||
tabKind: ViewModels.CollectionTabKind.Terminal,
|
||||
node: null,
|
||||
title: title,
|
||||
tabPath: title,
|
||||
documentClientUtility: null,
|
||||
|
||||
collection: null,
|
||||
selfLink: null,
|
||||
hashLocation: hashLocation,
|
||||
isActive: ko.observable(false),
|
||||
isTabsContentExpanded: ko.observable(true),
|
||||
onLoadStartKey: null,
|
||||
onUpdateTabsButtons: this.onUpdateTabsButtons,
|
||||
container: this,
|
||||
openedTabs: this.openedTabs(),
|
||||
kind: kind
|
||||
});
|
||||
|
||||
this.openedTabs.push(newTab);
|
||||
|
||||
// Activate
|
||||
newTab.onTabClick();
|
||||
}
|
||||
|
||||
public async openGallery(notebookUrl?: string, galleryItem?: IGalleryItem, isFavorite?: boolean) {
|
||||
let title: string;
|
||||
let hashLocation: string;
|
||||
let title: string = "Gallery";
|
||||
let hashLocation: string = "gallery";
|
||||
|
||||
title = "Gallery";
|
||||
hashLocation = "gallery";
|
||||
|
||||
const openedTabs = this.openedTabs().filter(
|
||||
(tab: ViewModels.Tab) => tab.tabKind === ViewModels.CollectionTabKind.Gallery
|
||||
const galleryTabs = this.tabsManager.getTabs(
|
||||
ViewModels.CollectionTabKind.Gallery,
|
||||
(tab: ViewModels.Tab) => tab.hashLocation() == hashLocation
|
||||
);
|
||||
let galleryTab = galleryTabs && galleryTabs[0];
|
||||
|
||||
for (let i = 0; i < openedTabs.length; ++i) {
|
||||
if (openedTabs[i].hashLocation() == hashLocation) {
|
||||
openedTabs[i].onTabClick();
|
||||
openedTabs[i].onActivate();
|
||||
(openedTabs[i] as any).updateGalleryParams(notebookUrl, galleryItem, isFavorite);
|
||||
return;
|
||||
if (galleryTab) {
|
||||
this.tabsManager.activateTab(galleryTab);
|
||||
(galleryTab as any).updateGalleryParams(notebookUrl, galleryItem, isFavorite);
|
||||
} else {
|
||||
if (!this.galleryTab) {
|
||||
this.galleryTab = await import(/* webpackChunkName: "GalleryTab" */ "./Tabs/GalleryTab");
|
||||
}
|
||||
|
||||
const newTab = new this.galleryTab.default({
|
||||
// GalleryTabOptions
|
||||
account: CosmosClient.databaseAccount(),
|
||||
container: this,
|
||||
junoClient: this.notebookManager?.junoClient,
|
||||
notebookUrl,
|
||||
galleryItem,
|
||||
isFavorite,
|
||||
// TabOptions
|
||||
tabKind: ViewModels.CollectionTabKind.Gallery,
|
||||
title: title,
|
||||
tabPath: title,
|
||||
documentClientUtility: null,
|
||||
selfLink: null,
|
||||
isActive: ko.observable(false),
|
||||
hashLocation: hashLocation,
|
||||
onUpdateTabsButtons: this.onUpdateTabsButtons,
|
||||
isTabsContentExpanded: ko.observable(true),
|
||||
onLoadStartKey: null
|
||||
});
|
||||
|
||||
this.tabsManager.activateNewTab(newTab);
|
||||
}
|
||||
|
||||
if (!this.galleryTab) {
|
||||
this.galleryTab = await import(/* webpackChunkName: "GalleryTab" */ "./Tabs/GalleryTab");
|
||||
}
|
||||
|
||||
const newTab = new this.galleryTab.default({
|
||||
// GalleryTabOptions
|
||||
account: CosmosClient.databaseAccount(),
|
||||
container: this,
|
||||
junoClient: this.notebookManager?.junoClient,
|
||||
notebookUrl,
|
||||
galleryItem,
|
||||
isFavorite,
|
||||
// TabOptions
|
||||
tabKind: ViewModels.CollectionTabKind.Gallery,
|
||||
title: title,
|
||||
tabPath: title,
|
||||
documentClientUtility: null,
|
||||
selfLink: null,
|
||||
isActive: ko.observable(false),
|
||||
hashLocation: hashLocation,
|
||||
onUpdateTabsButtons: this.onUpdateTabsButtons,
|
||||
isTabsContentExpanded: ko.observable(true),
|
||||
onLoadStartKey: null,
|
||||
openedTabs: this.openedTabs()
|
||||
});
|
||||
|
||||
this.openedTabs.push(newTab);
|
||||
|
||||
// Activate
|
||||
newTab.onTabClick();
|
||||
}
|
||||
|
||||
public async openNotebookViewer(notebookUrl: string) {
|
||||
|
@ -3189,41 +3147,37 @@ export default class Explorer implements ViewModels.Explorer {
|
|||
return notebookViewerTab.notebookUrl === notebookUrl;
|
||||
};
|
||||
|
||||
const openedTabs = this.openedTabs().filter(
|
||||
(tab: ViewModels.Tab) => tab.tabKind === ViewModels.CollectionTabKind.NotebookViewer && isNotebookViewerOpen(tab)
|
||||
);
|
||||
|
||||
for (let i = 0; i < openedTabs.length; ++i) {
|
||||
if (openedTabs[i].hashLocation() == hashLocation) {
|
||||
openedTabs[i].onTabClick();
|
||||
openedTabs[i].onActivate();
|
||||
return;
|
||||
const notebookViewerTabs = this.tabsManager.getTabs(
|
||||
ViewModels.CollectionTabKind.NotebookV2,
|
||||
(tab: ViewModels.Tab) => {
|
||||
return tab.hashLocation() == hashLocation && isNotebookViewerOpen(tab);
|
||||
}
|
||||
);
|
||||
let notebookViewerTab = notebookViewerTabs && notebookViewerTabs[0];
|
||||
|
||||
if (notebookViewerTab) {
|
||||
this.tabsManager.activateNewTab(notebookViewerTab);
|
||||
} else {
|
||||
notebookViewerTab = new this.notebookViewerTab.default({
|
||||
account: CosmosClient.databaseAccount(),
|
||||
tabKind: ViewModels.CollectionTabKind.NotebookViewer,
|
||||
node: null,
|
||||
title: title,
|
||||
tabPath: title,
|
||||
documentClientUtility: null,
|
||||
collection: null,
|
||||
selfLink: null,
|
||||
hashLocation: hashLocation,
|
||||
isActive: ko.observable(false),
|
||||
isTabsContentExpanded: ko.observable(true),
|
||||
onLoadStartKey: null,
|
||||
onUpdateTabsButtons: this.onUpdateTabsButtons,
|
||||
container: this,
|
||||
notebookUrl
|
||||
});
|
||||
|
||||
this.tabsManager.activateNewTab(notebookViewerTab);
|
||||
}
|
||||
|
||||
const newTab = new this.notebookViewerTab.default({
|
||||
account: CosmosClient.databaseAccount(),
|
||||
tabKind: ViewModels.CollectionTabKind.NotebookViewer,
|
||||
node: null,
|
||||
title: title,
|
||||
tabPath: title,
|
||||
documentClientUtility: null,
|
||||
collection: null,
|
||||
selfLink: null,
|
||||
hashLocation: hashLocation,
|
||||
isActive: ko.observable(false),
|
||||
isTabsContentExpanded: ko.observable(true),
|
||||
onLoadStartKey: null,
|
||||
onUpdateTabsButtons: this.onUpdateTabsButtons,
|
||||
container: this,
|
||||
openedTabs: this.openedTabs(),
|
||||
notebookUrl
|
||||
});
|
||||
|
||||
this.openedTabs.push(newTab);
|
||||
|
||||
// Activate
|
||||
newTab.onTabClick();
|
||||
}
|
||||
|
||||
public onNewCollectionClicked(): void {
|
||||
|
@ -3235,24 +3189,8 @@ export default class Explorer implements ViewModels.Explorer {
|
|||
document.getElementById("linkAddCollection").focus();
|
||||
}
|
||||
|
||||
private getNotebookTabsForFilepath(filepath: string): ViewModels.Tab[] {
|
||||
return this.openedTabs().filter(
|
||||
(tab: ViewModels.Tab) =>
|
||||
tab.tabKind === ViewModels.CollectionTabKind.NotebookV2 &&
|
||||
(tab as any).notebookPath &&
|
||||
FileSystemUtil.isPathEqual((tab as any).notebookPath(), filepath)
|
||||
);
|
||||
}
|
||||
|
||||
public closeNotebookTab(filepath: string): void {
|
||||
if (!filepath) {
|
||||
return;
|
||||
}
|
||||
this.getNotebookTabsForFilepath(filepath).forEach(tab => tab.onCloseTabButtonClick());
|
||||
}
|
||||
|
||||
private refreshCommandBarButtons(): void {
|
||||
const activeTab = this.findActiveTab();
|
||||
const activeTab = this.tabsManager.activeTab();
|
||||
if (activeTab) {
|
||||
activeTab.onActivate(); // TODO only update tabs buttons?
|
||||
} else {
|
||||
|
|
|
@ -16,10 +16,14 @@ export class CommandBarComponentAdapter implements ReactAdapter {
|
|||
public parameters: ko.Observable<number>;
|
||||
public container: ViewModels.Explorer;
|
||||
private tabsButtons: ViewModels.NavbarButtonConfig[];
|
||||
private isNotebookTabActive: ko.Computed<boolean>;
|
||||
|
||||
constructor(container: ViewModels.Explorer) {
|
||||
this.container = container;
|
||||
this.tabsButtons = [];
|
||||
this.isNotebookTabActive = ko.computed(() =>
|
||||
container.tabsManager.isTabActive(ViewModels.CollectionTabKind.NotebookV2)
|
||||
);
|
||||
|
||||
// These are the parameters watched by the react binding that will trigger a renderComponent() if one of the ko mutates
|
||||
const toWatch = [
|
||||
|
@ -39,7 +43,7 @@ export class CommandBarComponentAdapter implements ReactAdapter {
|
|||
container.isHostedDataExplorerEnabled,
|
||||
container.isSynapseLinkUpdating,
|
||||
container.databaseAccount,
|
||||
container.isNotebookTabActive
|
||||
this.isNotebookTabActive
|
||||
];
|
||||
|
||||
ko.computed(() => ko.toJSON(toWatch)).subscribe(() => this.triggerRender());
|
||||
|
@ -74,7 +78,7 @@ export class CommandBarComponentAdapter implements ReactAdapter {
|
|||
const uiFabricControlButtons = CommandBarUtil.convertButton(controlButtons, backgroundColor);
|
||||
uiFabricControlButtons.forEach((btn: ICommandBarItemProps) => (btn.iconOnly = true));
|
||||
|
||||
if (this.container && this.container.isNotebookTabActive()) {
|
||||
if (this.isNotebookTabActive()) {
|
||||
uiFabricControlButtons.unshift(
|
||||
CommandBarUtil.createMemoryTracker("memoryTracker", this.container.memoryUsageInfo)
|
||||
);
|
||||
|
|
|
@ -43,6 +43,7 @@ import { CdbAppState } from "./types";
|
|||
import { decryptJWTToken } from "../../../Utils/AuthorizationUtils";
|
||||
import * as TextFile from "./contents/file/text-file";
|
||||
import { NotebookUtil } from "../NotebookUtil";
|
||||
import { FileSystemUtil } from "../FileSystemUtil";
|
||||
|
||||
interface NotebookServiceConfig extends JupyterServerConfig {
|
||||
userPuid?: string;
|
||||
|
@ -806,7 +807,9 @@ const closeUnsupportedMimetypesEpic = (
|
|||
if (explorer && !TextFile.handles(mimetype)) {
|
||||
const filepath = action.payload.filepath;
|
||||
// Close tab and show error message
|
||||
explorer.closeNotebookTab(filepath);
|
||||
explorer.tabsManager.closeTabsByComparator(
|
||||
tab => (tab as any).notebookPath && FileSystemUtil.isPathEqual((tab as any).notebookPath(), filepath)
|
||||
);
|
||||
const msg = `${filepath} cannot be rendered. Please download the file, in order to view it outside of Data Explorer.`;
|
||||
explorer.showOkModalDialog("File cannot be rendered", msg);
|
||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, msg);
|
||||
|
@ -832,7 +835,9 @@ const closeContentFailedToFetchEpic = (
|
|||
if (explorer) {
|
||||
const filepath = action.payload.filepath;
|
||||
// Close tab and show error message
|
||||
explorer.closeNotebookTab(filepath);
|
||||
explorer.tabsManager.closeTabsByComparator(
|
||||
tab => (tab as any).notebookPath && FileSystemUtil.isPathEqual((tab as any).notebookPath(), filepath)
|
||||
);
|
||||
const msg = `Failed to load file: ${filepath}.`;
|
||||
explorer.showOkModalDialog("Failure to load", msg);
|
||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, msg);
|
||||
|
|
|
@ -22,6 +22,7 @@ import { Versions } from "../../src/Contracts/ExplorerContracts";
|
|||
import { CollectionCreationDefaults } from "../Shared/Constants";
|
||||
import { IGalleryItem } from "../Juno/JunoClient";
|
||||
import { ReactAdapter } from "../Bindings/ReactBindingHandler";
|
||||
import { TabsManager } from "./Tabs/TabsManager";
|
||||
|
||||
export class ExplorerStub implements ViewModels.Explorer {
|
||||
public flight: ko.Observable<string>;
|
||||
|
@ -69,7 +70,6 @@ export class ExplorerStub implements ViewModels.Explorer {
|
|||
public isLeftPaneExpanded: ko.Observable<boolean>;
|
||||
public selectedNode: ko.Observable<ViewModels.TreeNode>;
|
||||
public isRefreshingExplorer: ko.Observable<boolean>;
|
||||
public openedTabs: ko.ObservableArray<ViewModels.Tab>;
|
||||
public isTabsContentExpanded: ko.Observable<boolean>;
|
||||
public addCollectionPane: ViewModels.AddCollectionPane;
|
||||
public addDatabasePane: ViewModels.AddDatabasePane;
|
||||
|
@ -101,7 +101,6 @@ export class ExplorerStub implements ViewModels.Explorer {
|
|||
public canExceedMaximumValue: ko.Computed<boolean>;
|
||||
public isHostedDataExplorerEnabled: ko.Computed<boolean>;
|
||||
public parentFrameDataExplorerVersion: ko.Observable<string> = ko.observable<string>(Versions.DataExplorer);
|
||||
public activeTab: ko.Observable<ViewModels.Tab>;
|
||||
public mostRecentActivity: MostRecentActivity;
|
||||
public isNotebookEnabled: ko.Observable<boolean>;
|
||||
public isSparkEnabled: ko.Observable<boolean>;
|
||||
|
@ -119,7 +118,6 @@ export class ExplorerStub implements ViewModels.Explorer {
|
|||
public arcadiaWorkspaces: ko.ObservableArray<ArcadiaWorkspaceItem>;
|
||||
public hasStorageAnalyticsAfecFeature: ko.Observable<boolean>;
|
||||
public isSynapseLinkUpdating: ko.Observable<boolean>;
|
||||
public isNotebookTabActive: ko.Computed<boolean>;
|
||||
public memoryUsageInfo: ko.Observable<DataModels.MemoryUsageInfo>;
|
||||
public notebookManager?: any;
|
||||
public openGallery: (notebookUrl?: string, galleryItem?: IGalleryItem, isFavorite?: boolean) => void;
|
||||
|
@ -130,6 +128,7 @@ export class ExplorerStub implements ViewModels.Explorer {
|
|||
public resourceTokenPartitionKey: ko.Observable<string>;
|
||||
public isAuthWithResourceToken: ko.Observable<boolean>;
|
||||
public isResourceTokenCollectionNodeSelected: ko.Computed<boolean>;
|
||||
public tabsManager: TabsManager;
|
||||
|
||||
private _featureEnabledReturnValue: boolean;
|
||||
|
||||
|
@ -248,10 +247,6 @@ export class ExplorerStub implements ViewModels.Explorer {
|
|||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
public findActiveTab(): ViewModels.Tab {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
public findSelectedStoredProcedure(): ViewModels.StoredProcedure {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
@ -300,10 +295,6 @@ export class ExplorerStub implements ViewModels.Explorer {
|
|||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
public closeAllTabsForResource(resourceId: string): void {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
public getPlatformType(): PlatformType {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
@ -426,10 +417,6 @@ export class ExplorerStub implements ViewModels.Explorer {
|
|||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
public closeNotebookTab(filepath: string): void {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
public refreshContentItem(item: NotebookContentItem): Promise<void> {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
@ -511,10 +498,6 @@ export class DatabaseStub implements ViewModels.Database {
|
|||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
public refreshTabSelectedState(): void {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
public readSettings() {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
@ -779,10 +762,6 @@ export class CollectionStub implements ViewModels.Collection {
|
|||
throw new Error("Not implemented");
|
||||
};
|
||||
|
||||
public refreshActiveTab = (): void => {
|
||||
throw new Error("Not implemented");
|
||||
};
|
||||
|
||||
public getLabel(): string {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ export class BrowseQueriesPane extends ContextualPaneBase implements ViewModels.
|
|||
} else {
|
||||
selectedCollection.onNewQueryClick(selectedCollection, null);
|
||||
}
|
||||
const queryTab: ViewModels.QueryTab = this.container.findActiveTab() as ViewModels.QueryTab;
|
||||
const queryTab: ViewModels.QueryTab = this.container.tabsManager.activeTab() as ViewModels.QueryTab;
|
||||
queryTab.tabTitle(savedQuery.queryName);
|
||||
queryTab.tabPath(`${selectedCollection.databaseId}>${selectedCollection.id()}>${savedQuery.queryName}`);
|
||||
queryTab.initialEditorContent(savedQuery.query);
|
||||
|
|
|
@ -66,7 +66,9 @@ export default class DeleteCollectionConfirmationPane extends ContextualPaneBase
|
|||
this.isExecuting(false);
|
||||
this.close();
|
||||
this.container.selectedNode(selectedCollection.database);
|
||||
this.container.closeAllTabsForResource(selectedCollection.rid);
|
||||
this.container.tabsManager?.closeTabsByComparator(
|
||||
(tab: ViewModels.Tab) => tab.node && tab.node.rid === selectedCollection.rid
|
||||
);
|
||||
this.container.refreshAllDatabases();
|
||||
this.resetData();
|
||||
TelemetryProcessor.traceSuccess(
|
||||
|
|
|
@ -11,6 +11,7 @@ import Explorer from "../Explorer";
|
|||
import { CollectionStub, DatabaseStub, ExplorerStub } from "../OpenActionsStubs";
|
||||
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { TreeNode } from "../../Contracts/ViewModels";
|
||||
import { TabsManager } from "../Tabs/TabsManager";
|
||||
|
||||
describe("Delete Database Confirmation Pane", () => {
|
||||
describe("Explorer.isLastDatabase() and Explorer.isLastNonEmptyDatabase()", () => {
|
||||
|
@ -123,6 +124,7 @@ describe("Delete Database Confirmation Pane", () => {
|
|||
);
|
||||
sinon.stub(fakeExplorer, "documentClientUtility").value(fakeDocumentClientUtility);
|
||||
sinon.stub(fakeExplorer, "selectedNode").value(ko.observable<TreeNode>());
|
||||
sinon.stub(fakeExplorer, "tabsManager").value(new TabsManager());
|
||||
fakeExplorer.isLastNonEmptyDatabase.returns(true);
|
||||
|
||||
let pane = new DeleteDatabaseConfirmationPane({
|
||||
|
|
|
@ -67,11 +67,17 @@ export default class DeleteDatabaseConfirmationPane extends ContextualPaneBase
|
|||
this.isExecuting(false);
|
||||
this.close();
|
||||
this.container.refreshAllDatabases();
|
||||
this.container.closeAllTabsForResource(selectedDatabase.rid);
|
||||
this.container.tabsManager.closeTabsByComparator(
|
||||
(tab: ViewModels.Tab) => tab.node && tab.node.rid === selectedDatabase.rid
|
||||
);
|
||||
this.container.selectedNode(null);
|
||||
selectedDatabase
|
||||
.collections()
|
||||
.forEach((collection: ViewModels.Collection) => this.container.closeAllTabsForResource(collection.rid));
|
||||
.forEach((collection: ViewModels.Collection) =>
|
||||
this.container.tabsManager.closeTabsByComparator(
|
||||
(tab: ViewModels.Tab) => tab.node && tab.node.rid === collection.rid
|
||||
)
|
||||
);
|
||||
this.resetData();
|
||||
TelemetryProcessor.traceSuccess(
|
||||
Action.DeleteDatabase,
|
||||
|
|
|
@ -111,7 +111,7 @@ export class LoadQueryPane extends ContextualPaneBase implements ViewModels.Load
|
|||
const reader = new FileReader();
|
||||
reader.onload = (evt: any): void => {
|
||||
const fileData: string = evt.target.result;
|
||||
const queryTab: ViewModels.QueryTab = this.container.findActiveTab() as ViewModels.QueryTab;
|
||||
const queryTab: ViewModels.QueryTab = this.container.tabsManager.activeTab() as ViewModels.QueryTab;
|
||||
queryTab.initialEditorContent(fileData);
|
||||
queryTab.sqlQueryEditorContent(fileData);
|
||||
deferred.resolve();
|
||||
|
|
|
@ -53,7 +53,7 @@ export class RenewAdHocAccessPane extends ContextualPaneBase implements ViewMode
|
|||
const apiKind: DataModels.ApiKind =
|
||||
this.container && DefaultExperienceUtility.getApiKindFromDefaultExperience(this.container.defaultExperience());
|
||||
const hasOpenedTabs: boolean =
|
||||
(this.container && this.container.openedTabs() && this.container.openedTabs().length > 0) || false;
|
||||
(this.container && this.container.tabsManager && this.container.tabsManager.openedTabs().length > 0) || false;
|
||||
|
||||
if (!inputMetadata || inputMetadata.apiKind == null || !inputMetadata.accountName) {
|
||||
this.formErrors("Invalid connection string input");
|
||||
|
|
|
@ -34,7 +34,8 @@ export class SaveQueryPane extends ContextualPaneBase {
|
|||
}
|
||||
|
||||
const queryName: string = this.queryName();
|
||||
const queryTab: ViewModels.QueryTab = this.container && (this.container.findActiveTab() as ViewModels.QueryTab);
|
||||
const queryTab: ViewModels.QueryTab =
|
||||
this.container && (this.container.tabsManager.activeTab() as ViewModels.QueryTab);
|
||||
const query: string = queryTab && queryTab.sqlQueryEditorContent();
|
||||
if (!queryName || queryName.length === 0) {
|
||||
this.formErrors("No query name specified");
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
import * as ko from "knockout";
|
||||
import { DataSamplesUtil } from "../DataSamples/DataSamplesUtil";
|
||||
import { SplashScreenComponentAdapter } from "./SplashScreenComponentApdapter";
|
||||
import { TabsManager } from "../Tabs/TabsManager";
|
||||
import Explorer from "../Explorer";
|
||||
jest.mock("../Explorer");
|
||||
|
||||
const createExplorer = () => {
|
||||
const mock = new Explorer({} as any);
|
||||
mock.openedTabs = ko.observableArray([]);
|
||||
mock.selectedNode = ko.observable();
|
||||
mock.isNotebookEnabled = ko.observable(false);
|
||||
mock.addCollectionText = ko.observable("add collection");
|
||||
mock.tabsManager = new TabsManager();
|
||||
return mock as jest.Mocked<Explorer>;
|
||||
};
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ export class SplashScreenComponentAdapter implements ReactAdapter {
|
|||
|
||||
constructor(private container: ViewModels.Explorer) {
|
||||
this.parameters = ko.observable<number>(Date.now());
|
||||
this.container.openedTabs.subscribe(tabs => {
|
||||
this.container.tabsManager.openedTabs.subscribe((tabs: ViewModels.Tab[]) => {
|
||||
if (tabs.length === 0) {
|
||||
this.forceRender();
|
||||
}
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 => {}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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", () => {
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
|
@ -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);
|
||||
});
|
||||
});
|
|
@ -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
|
||||
};
|
||||
}
|
|
@ -16,7 +16,10 @@ import { OfferUtils } from "../../Utils/OfferUtils";
|
|||
import { StartUploadMessageParams, UploadDetails, UploadDetailsRecord } from "../../workers/upload/definitions";
|
||||
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||
import { CassandraAPIDataClient, CassandraTableKey, CassandraTableKeys } from "../Tables/TableDataClient";
|
||||
import { ConflictsTab } from "../Tabs/ConflictsTab";
|
||||
import ConflictId from "./ConflictId";
|
||||
|
||||
import DocumentId from "./DocumentId";
|
||||
import ConflictsTab from "../Tabs/ConflictsTab";
|
||||
import DocumentsTab from "../Tabs/DocumentsTab";
|
||||
import GraphTab from "../Tabs/GraphTab";
|
||||
import MongoDocumentsTab from "../Tabs/MongoDocumentsTab";
|
||||
|
@ -25,8 +28,6 @@ import MongoShellTab from "../Tabs/MongoShellTab";
|
|||
import QueryTab from "../Tabs/QueryTab";
|
||||
import QueryTablesTab from "../Tabs/QueryTablesTab";
|
||||
import SettingsTab from "../Tabs/SettingsTab";
|
||||
import ConflictId from "./ConflictId";
|
||||
import DocumentId from "./DocumentId";
|
||||
import StoredProcedure from "./StoredProcedure";
|
||||
import Trigger from "./Trigger";
|
||||
import UserDefinedFunction from "./UserDefinedFunction";
|
||||
|
@ -229,7 +230,9 @@ export default class Collection implements ViewModels.Collection {
|
|||
this.expandCollection();
|
||||
}
|
||||
this.container.onUpdateTabsButtons([]);
|
||||
this.refreshActiveTab();
|
||||
this.container.tabsManager.refreshActiveTab(
|
||||
(tab: ViewModels.Tab) => tab.collection && tab.collection.rid === this.rid
|
||||
);
|
||||
}
|
||||
|
||||
public collapseCollection() {
|
||||
|
@ -278,14 +281,15 @@ export default class Collection implements ViewModels.Collection {
|
|||
dataExplorerArea: Constants.Areas.ResourceTree
|
||||
});
|
||||
|
||||
// create documents tab if not created yet
|
||||
const openedTabs = this.container.openedTabs();
|
||||
const documentsTabs: DocumentsTab[] = this.container.tabsManager.getTabs(
|
||||
ViewModels.CollectionTabKind.Documents,
|
||||
(tab: ViewModels.Tab) => tab.collection && tab.collection.rid === this.rid
|
||||
) as DocumentsTab[];
|
||||
let documentsTab: DocumentsTab = documentsTabs && documentsTabs[0];
|
||||
|
||||
let documentsTab: ViewModels.Tab = openedTabs
|
||||
.filter(tab => tab.collection && tab.collection.rid === this.rid)
|
||||
.filter(tab => tab.tabKind === ViewModels.CollectionTabKind.Documents)[0];
|
||||
|
||||
if (!documentsTab) {
|
||||
if (documentsTab) {
|
||||
this.container.tabsManager.activateTab(documentsTab);
|
||||
} else {
|
||||
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
||||
databaseAccountName: this.container.databaseAccount().name,
|
||||
databaseName: this.databaseId,
|
||||
|
@ -295,6 +299,7 @@ export default class Collection implements ViewModels.Collection {
|
|||
tabTitle: "Items"
|
||||
});
|
||||
this.documentIds([]);
|
||||
|
||||
documentsTab = new DocumentsTab({
|
||||
partitionKey: this.partitionKey,
|
||||
documentIds: ko.observableArray<DocumentId>([]),
|
||||
|
@ -309,14 +314,11 @@ export default class Collection implements ViewModels.Collection {
|
|||
tabPath: `${this.databaseId}>${this.id()}>Documents`,
|
||||
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/documents`,
|
||||
onLoadStartKey: startKey,
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
||||
openedTabs: this.container.openedTabs()
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||
});
|
||||
this.container.openedTabs.push(documentsTab);
|
||||
}
|
||||
|
||||
// Activate
|
||||
documentsTab.onTabClick();
|
||||
this.container.tabsManager.activateNewTab(documentsTab);
|
||||
}
|
||||
}
|
||||
|
||||
public onConflictsClick() {
|
||||
|
@ -331,14 +333,15 @@ export default class Collection implements ViewModels.Collection {
|
|||
dataExplorerArea: Constants.Areas.ResourceTree
|
||||
});
|
||||
|
||||
// create documents tab if not created yet
|
||||
const openedTabs = this.container.openedTabs();
|
||||
const conflictsTabs: ConflictsTab[] = this.container.tabsManager.getTabs(
|
||||
ViewModels.CollectionTabKind.Conflicts,
|
||||
(tab: ViewModels.Tab) => tab.collection && tab.collection.rid === this.rid
|
||||
) as ConflictsTab[];
|
||||
let conflictsTab: ConflictsTab = conflictsTabs && conflictsTabs[0];
|
||||
|
||||
let conflictsTab: ViewModels.Tab = openedTabs
|
||||
.filter(tab => tab.collection && tab.collection.rid === this.rid)
|
||||
.filter(tab => tab.tabKind === ViewModels.CollectionTabKind.Conflicts)[0];
|
||||
|
||||
if (!conflictsTab) {
|
||||
if (conflictsTab) {
|
||||
this.container.tabsManager.activateTab(conflictsTab);
|
||||
} else {
|
||||
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
||||
databaseAccountName: this.container.databaseAccount().name,
|
||||
databaseName: this.databaseId,
|
||||
|
@ -348,7 +351,8 @@ export default class Collection implements ViewModels.Collection {
|
|||
tabTitle: "Conflicts"
|
||||
});
|
||||
this.documentIds([]);
|
||||
conflictsTab = new ConflictsTab({
|
||||
|
||||
const conflictsTab: ConflictsTab = new ConflictsTab({
|
||||
partitionKey: this.partitionKey,
|
||||
conflictIds: ko.observableArray<ConflictId>([]),
|
||||
tabKind: ViewModels.CollectionTabKind.Conflicts,
|
||||
|
@ -362,14 +366,11 @@ export default class Collection implements ViewModels.Collection {
|
|||
tabPath: `${this.databaseId}>${this.id()}>Conflicts`,
|
||||
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/conflicts`,
|
||||
onLoadStartKey: startKey,
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
||||
openedTabs: this.container.openedTabs()
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||
});
|
||||
this.container.openedTabs.push(conflictsTab);
|
||||
}
|
||||
|
||||
// Activate
|
||||
conflictsTab.onTabClick();
|
||||
this.container.tabsManager.activateNewTab(conflictsTab);
|
||||
}
|
||||
}
|
||||
|
||||
public onTableEntitiesClick() {
|
||||
|
@ -390,14 +391,15 @@ export default class Collection implements ViewModels.Collection {
|
|||
});
|
||||
}
|
||||
|
||||
// create entities tab if not created yet
|
||||
const openedTabs = this.container.openedTabs();
|
||||
const queryTablesTabs: QueryTablesTab[] = this.container.tabsManager.getTabs(
|
||||
ViewModels.CollectionTabKind.QueryTables,
|
||||
(tab: ViewModels.Tab) => tab.collection && tab.collection.rid === this.rid
|
||||
) as QueryTablesTab[];
|
||||
let queryTablesTab: QueryTablesTab = queryTablesTabs && queryTablesTabs[0];
|
||||
|
||||
let documentsTab: ViewModels.Tab = openedTabs
|
||||
.filter(tab => tab.collection && tab.collection.rid === this.rid)
|
||||
.filter(tab => tab.tabKind === ViewModels.CollectionTabKind.QueryTables)[0];
|
||||
|
||||
if (!documentsTab) {
|
||||
if (queryTablesTab) {
|
||||
this.container.tabsManager.activateTab(queryTablesTab);
|
||||
} else {
|
||||
this.documentIds([]);
|
||||
let title = `Entities`;
|
||||
if (this.container.isPreferredApiCassandra()) {
|
||||
|
@ -411,7 +413,8 @@ export default class Collection implements ViewModels.Collection {
|
|||
dataExplorerArea: Constants.Areas.Tab,
|
||||
tabTitle: title
|
||||
});
|
||||
documentsTab = new QueryTablesTab({
|
||||
|
||||
queryTablesTab = new QueryTablesTab({
|
||||
tabKind: ViewModels.CollectionTabKind.QueryTables,
|
||||
title: title,
|
||||
tabPath: "",
|
||||
|
@ -423,14 +426,11 @@ export default class Collection implements ViewModels.Collection {
|
|||
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/entities`,
|
||||
isActive: ko.observable(false),
|
||||
onLoadStartKey: startKey,
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
||||
openedTabs: this.container.openedTabs()
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||
});
|
||||
this.container.openedTabs.push(documentsTab);
|
||||
}
|
||||
|
||||
// Activate
|
||||
documentsTab.onTabClick();
|
||||
this.container.tabsManager.activateNewTab(queryTablesTab);
|
||||
}
|
||||
}
|
||||
|
||||
public onGraphDocumentsClick() {
|
||||
|
@ -445,55 +445,50 @@ export default class Collection implements ViewModels.Collection {
|
|||
dataExplorerArea: Constants.Areas.ResourceTree
|
||||
});
|
||||
|
||||
// create documents tab if not created yet
|
||||
const openedTabs = this.container.openedTabs();
|
||||
const graphTabs: GraphTab[] = this.container.tabsManager.getTabs(
|
||||
ViewModels.CollectionTabKind.Graph,
|
||||
(tab: ViewModels.Tab) => tab.collection && tab.collection.rid === this.rid
|
||||
) as GraphTab[];
|
||||
let graphTab: GraphTab = graphTabs && graphTabs[0];
|
||||
|
||||
let documentsTab: ViewModels.Tab = openedTabs
|
||||
.filter(tab => tab.collection && tab.collection.rid === this.rid)
|
||||
.filter(tab => tab.tabKind === ViewModels.CollectionTabKind.Graph)[0];
|
||||
|
||||
if (!documentsTab) {
|
||||
if (graphTab) {
|
||||
this.container.tabsManager.activateTab(graphTab);
|
||||
} else {
|
||||
this.documentIds([]);
|
||||
documentsTab = this._createGraphTab("Graph");
|
||||
this.container.openedTabs.push(documentsTab);
|
||||
const title = "Graph";
|
||||
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
||||
databaseAccountName: this.container.databaseAccount().name,
|
||||
databaseName: this.databaseId,
|
||||
collectionName: this.id(),
|
||||
defaultExperience: this.container.defaultExperience(),
|
||||
dataExplorerArea: Constants.Areas.Tab,
|
||||
tabTitle: title
|
||||
});
|
||||
|
||||
graphTab = new GraphTab({
|
||||
account: CosmosClient.databaseAccount(),
|
||||
tabKind: ViewModels.CollectionTabKind.Graph,
|
||||
node: this,
|
||||
title: title,
|
||||
tabPath: "",
|
||||
documentClientUtility: this.container.documentClientUtility,
|
||||
collection: this,
|
||||
selfLink: this.self,
|
||||
masterKey: CosmosClient.masterKey() || "",
|
||||
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,
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||
});
|
||||
|
||||
this.container.tabsManager.activateNewTab(graphTab);
|
||||
}
|
||||
|
||||
// Activate
|
||||
documentsTab.onTabClick();
|
||||
}
|
||||
|
||||
private _createGraphTab = (title: string): ViewModels.GraphTab => {
|
||||
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
||||
databaseAccountName: this.container.databaseAccount().name,
|
||||
databaseName: this.databaseId,
|
||||
collectionName: this.id(),
|
||||
defaultExperience: this.container.defaultExperience(),
|
||||
dataExplorerArea: Constants.Areas.Tab,
|
||||
tabTitle: title
|
||||
});
|
||||
// TODO where does the success/failure trace for this tab go?
|
||||
return new GraphTab({
|
||||
account: CosmosClient.databaseAccount(),
|
||||
tabKind: ViewModels.CollectionTabKind.Graph,
|
||||
node: this,
|
||||
title: title,
|
||||
tabPath: "",
|
||||
documentClientUtility: this.container.documentClientUtility,
|
||||
collection: this,
|
||||
selfLink: this.self,
|
||||
masterKey: CosmosClient.masterKey() || "",
|
||||
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,
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
||||
openedTabs: this.container.openedTabs()
|
||||
});
|
||||
};
|
||||
|
||||
public onMongoDBDocumentsClick = () => {
|
||||
this.container.selectedNode(this);
|
||||
this.selectedSubnodeKind(ViewModels.CollectionTabKind.Documents);
|
||||
|
@ -506,14 +501,15 @@ export default class Collection implements ViewModels.Collection {
|
|||
dataExplorerArea: Constants.Areas.ResourceTree
|
||||
});
|
||||
|
||||
// create documents tab if not created yet
|
||||
const openedTabs = this.container.openedTabs();
|
||||
const mongoDocumentsTabs: MongoDocumentsTab[] = this.container.tabsManager.getTabs(
|
||||
ViewModels.CollectionTabKind.Documents,
|
||||
(tab: ViewModels.Tab) => tab.collection && tab.collection.rid === this.rid
|
||||
) as MongoDocumentsTab[];
|
||||
let mongoDocumentsTab: MongoDocumentsTab = mongoDocumentsTabs && mongoDocumentsTabs[0];
|
||||
|
||||
let documentsTab: ViewModels.Tab = openedTabs
|
||||
.filter(tab => tab.collection && tab.collection && tab.collection.rid === this.rid)
|
||||
.filter(tab => tab.tabKind === ViewModels.CollectionTabKind.Documents)[0];
|
||||
|
||||
if (!documentsTab) {
|
||||
if (mongoDocumentsTab) {
|
||||
this.container.tabsManager.activateTab(mongoDocumentsTab);
|
||||
} else {
|
||||
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
||||
databaseAccountName: this.container.databaseAccount().name,
|
||||
databaseName: this.databaseId,
|
||||
|
@ -523,7 +519,8 @@ export default class Collection implements ViewModels.Collection {
|
|||
tabTitle: "Documents"
|
||||
});
|
||||
this.documentIds([]);
|
||||
documentsTab = new MongoDocumentsTab({
|
||||
|
||||
mongoDocumentsTab = new MongoDocumentsTab({
|
||||
partitionKey: this.partitionKey,
|
||||
documentIds: this.documentIds,
|
||||
tabKind: ViewModels.CollectionTabKind.Documents,
|
||||
|
@ -537,14 +534,10 @@ export default class Collection implements ViewModels.Collection {
|
|||
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/mongoDocuments`,
|
||||
isActive: ko.observable(false),
|
||||
onLoadStartKey: startKey,
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
||||
openedTabs: this.container.openedTabs()
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||
});
|
||||
this.container.openedTabs.push(documentsTab);
|
||||
this.container.tabsManager.activateNewTab(mongoDocumentsTab);
|
||||
}
|
||||
|
||||
// Activate
|
||||
documentsTab.onTabClick();
|
||||
};
|
||||
|
||||
public onSettingsClick = () => {
|
||||
|
@ -559,14 +552,15 @@ export default class Collection implements ViewModels.Collection {
|
|||
dataExplorerArea: Constants.Areas.ResourceTree
|
||||
});
|
||||
|
||||
// create settings tab if not created yet
|
||||
const openedTabs = this.container.openedTabs();
|
||||
let settingsTab: ViewModels.Tab = openedTabs
|
||||
.filter(tab => tab.collection && tab.collection.rid === this.rid)
|
||||
.filter(tab => tab.tabKind === ViewModels.CollectionTabKind.Settings)[0];
|
||||
|
||||
const tabTitle = "Scale & Settings";
|
||||
const pendingNotificationsPromise: Q.Promise<DataModels.Notification> = this._getPendingThroughputSplitNotification();
|
||||
const matchingTabs: ViewModels.Tab[] = this.container.tabsManager.getTabs(
|
||||
ViewModels.CollectionTabKind.Settings,
|
||||
(tab: ViewModels.Tab) => {
|
||||
return tab.collection && tab.collection.rid === this.rid;
|
||||
}
|
||||
);
|
||||
let settingsTab: SettingsTab = matchingTabs && (matchingTabs[0] as SettingsTab);
|
||||
if (!settingsTab) {
|
||||
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
||||
databaseAccountName: this.container.databaseAccount().name,
|
||||
|
@ -592,12 +586,10 @@ export default class Collection implements ViewModels.Collection {
|
|||
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/settings`,
|
||||
isActive: ko.observable(false),
|
||||
onLoadStartKey: startKey,
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
||||
openedTabs: this.container.openedTabs()
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||
});
|
||||
(settingsTab as ViewModels.SettingsTab).pendingNotification(pendingNotification);
|
||||
this.container.openedTabs.push(settingsTab);
|
||||
settingsTab.onTabClick(); // Activate
|
||||
this.container.tabsManager.activateNewTab(settingsTab);
|
||||
settingsTab.pendingNotification(pendingNotification);
|
||||
},
|
||||
(error: any) => {
|
||||
TelemetryProcessor.traceFailure(
|
||||
|
@ -623,12 +615,12 @@ export default class Collection implements ViewModels.Collection {
|
|||
} else {
|
||||
pendingNotificationsPromise.then(
|
||||
(pendingNotification: DataModels.Notification) => {
|
||||
(settingsTab as ViewModels.SettingsTab).pendingNotification(pendingNotification);
|
||||
settingsTab.onTabClick();
|
||||
settingsTab.pendingNotification(pendingNotification);
|
||||
this.container.tabsManager.activateTab(settingsTab);
|
||||
},
|
||||
(error: any) => {
|
||||
(settingsTab as ViewModels.SettingsTab).pendingNotification(undefined);
|
||||
settingsTab.onTabClick();
|
||||
settingsTab.pendingNotification(undefined);
|
||||
this.container.tabsManager.activateTab(settingsTab);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -738,9 +730,7 @@ export default class Collection implements ViewModels.Collection {
|
|||
|
||||
public onNewQueryClick(source: any, event: MouseEvent, queryText?: string) {
|
||||
const collection: ViewModels.Collection = source.collection || source;
|
||||
const explorer: ViewModels.Explorer = source.container;
|
||||
const openedTabs = explorer.openedTabs();
|
||||
const id = openedTabs.filter(t => t.tabKind === ViewModels.CollectionTabKind.Query).length + 1;
|
||||
const id = this.container.tabsManager.getTabs(ViewModels.CollectionTabKind.Query).length + 1;
|
||||
const title = "Query " + id;
|
||||
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
||||
databaseAccountName: this.container.databaseAccount().name,
|
||||
|
@ -751,7 +741,7 @@ export default class Collection implements ViewModels.Collection {
|
|||
tabTitle: title
|
||||
});
|
||||
|
||||
let queryTab: ViewModels.Tab = new QueryTab({
|
||||
const queryTab: QueryTab = new QueryTab({
|
||||
tabKind: ViewModels.CollectionTabKind.Query,
|
||||
title: title,
|
||||
tabPath: "",
|
||||
|
@ -764,20 +754,15 @@ export default class Collection implements ViewModels.Collection {
|
|||
queryText: queryText,
|
||||
partitionKey: collection.partitionKey,
|
||||
onLoadStartKey: startKey,
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
||||
openedTabs: this.container.openedTabs()
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||
});
|
||||
this.container.openedTabs.push(queryTab);
|
||||
|
||||
// Activate
|
||||
queryTab.onTabClick();
|
||||
this.container.tabsManager.activateNewTab(queryTab);
|
||||
}
|
||||
|
||||
public onNewMongoQueryClick(source: any, event: MouseEvent, queryText?: string) {
|
||||
const collection: ViewModels.Collection = source.collection || source;
|
||||
const explorer: ViewModels.Explorer = source.container;
|
||||
const openedTabs = explorer.openedTabs();
|
||||
const id = openedTabs.filter(t => t.tabKind === ViewModels.CollectionTabKind.Query).length + 1;
|
||||
const id = this.container.tabsManager.getTabs(ViewModels.CollectionTabKind.Query).length + 1;
|
||||
|
||||
const title = "Query " + id;
|
||||
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
||||
|
@ -789,7 +774,7 @@ export default class Collection implements ViewModels.Collection {
|
|||
tabTitle: title
|
||||
});
|
||||
|
||||
let queryTab: ViewModels.Tab = new MongoQueryTab({
|
||||
const mongoQueryTab: MongoQueryTab = new MongoQueryTab({
|
||||
tabKind: ViewModels.CollectionTabKind.Query,
|
||||
title: title,
|
||||
tabPath: "",
|
||||
|
@ -801,26 +786,51 @@ export default class Collection implements ViewModels.Collection {
|
|||
isActive: ko.observable(false),
|
||||
partitionKey: collection.partitionKey,
|
||||
onLoadStartKey: startKey,
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
||||
openedTabs: this.container.openedTabs()
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||
});
|
||||
this.container.openedTabs.push(queryTab);
|
||||
|
||||
// Activate
|
||||
queryTab.onTabClick();
|
||||
this.container.tabsManager.activateNewTab(mongoQueryTab);
|
||||
}
|
||||
|
||||
public onNewGraphClick() {
|
||||
var id = this.container.openedTabs().filter(t => t.tabKind === ViewModels.CollectionTabKind.Graph).length + 1;
|
||||
var graphTab = this._createGraphTab("Graph Query " + id);
|
||||
this.container.openedTabs.push(graphTab);
|
||||
// Activate
|
||||
graphTab.onTabClick();
|
||||
const id: number = this.container.tabsManager.getTabs(ViewModels.CollectionTabKind.Graph).length + 1;
|
||||
const title: string = "Graph Query " + id;
|
||||
|
||||
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
||||
databaseAccountName: this.container.databaseAccount().name,
|
||||
databaseName: this.databaseId,
|
||||
collectionName: this.id(),
|
||||
defaultExperience: this.container.defaultExperience(),
|
||||
dataExplorerArea: Constants.Areas.Tab,
|
||||
tabTitle: title
|
||||
});
|
||||
|
||||
const graphTab: GraphTab = new GraphTab({
|
||||
account: CosmosClient.databaseAccount(),
|
||||
tabKind: ViewModels.CollectionTabKind.Graph,
|
||||
node: this,
|
||||
title: title,
|
||||
tabPath: "",
|
||||
documentClientUtility: this.container.documentClientUtility,
|
||||
collection: this,
|
||||
selfLink: this.self,
|
||||
masterKey: CosmosClient.masterKey() || "",
|
||||
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,
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||
});
|
||||
|
||||
this.container.tabsManager.activateNewTab(graphTab);
|
||||
}
|
||||
|
||||
public onNewMongoShellClick() {
|
||||
var id = this.container.openedTabs().filter(t => t.tabKind === ViewModels.CollectionTabKind.MongoShell).length + 1;
|
||||
var mongoShellTab = new MongoShellTab({
|
||||
const id = this.container.tabsManager.getTabs(ViewModels.CollectionTabKind.MongoShell).length + 1;
|
||||
const mongoShellTab: MongoShellTab = new MongoShellTab({
|
||||
tabKind: ViewModels.CollectionTabKind.MongoShell,
|
||||
title: "Shell " + id,
|
||||
tabPath: "",
|
||||
|
@ -830,14 +840,10 @@ export default class Collection implements ViewModels.Collection {
|
|||
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/mongoShell`,
|
||||
selfLink: this.self,
|
||||
isActive: ko.observable(false),
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
||||
openedTabs: this.container.openedTabs()
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||
});
|
||||
|
||||
this.container.openedTabs.push(mongoShellTab);
|
||||
|
||||
// Activate
|
||||
mongoShellTab.onTabClick();
|
||||
this.container.tabsManager.activateNewTab(mongoShellTab);
|
||||
}
|
||||
|
||||
public onNewStoredProcedureClick(source: ViewModels.Collection, event: MouseEvent) {
|
||||
|
@ -898,7 +904,9 @@ export default class Collection implements ViewModels.Collection {
|
|||
} else {
|
||||
this.expandStoredProcedures();
|
||||
}
|
||||
this.refreshActiveTab();
|
||||
this.container.tabsManager.refreshActiveTab(
|
||||
(tab: ViewModels.Tab) => tab.collection && tab.collection.rid === this.rid
|
||||
);
|
||||
}
|
||||
|
||||
public expandStoredProcedures() {
|
||||
|
@ -955,7 +963,9 @@ export default class Collection implements ViewModels.Collection {
|
|||
} else {
|
||||
this.expandUserDefinedFunctions();
|
||||
}
|
||||
this.refreshActiveTab();
|
||||
this.container.tabsManager.refreshActiveTab(
|
||||
(tab: ViewModels.Tab) => tab.collection && tab.collection.rid === this.rid
|
||||
);
|
||||
}
|
||||
|
||||
public expandUserDefinedFunctions() {
|
||||
|
@ -1012,7 +1022,9 @@ export default class Collection implements ViewModels.Collection {
|
|||
} else {
|
||||
this.expandTriggers();
|
||||
}
|
||||
this.refreshActiveTab();
|
||||
this.container.tabsManager.refreshActiveTab(
|
||||
(tab: ViewModels.Tab) => tab.collection && tab.collection.rid === this.rid
|
||||
);
|
||||
}
|
||||
|
||||
public expandTriggers() {
|
||||
|
@ -1366,19 +1378,6 @@ export default class Collection implements ViewModels.Collection {
|
|||
});
|
||||
}
|
||||
|
||||
public refreshActiveTab(): void {
|
||||
// ensures that the tab selects/highlights the right node based on resource tree expand/collapse state
|
||||
const openedRelevantTabs: ViewModels.Tab[] = this.container
|
||||
.openedTabs()
|
||||
.filter((tab: ViewModels.Tab) => tab && tab.collection && tab.collection.rid === this.rid);
|
||||
|
||||
openedRelevantTabs.forEach((tab: ViewModels.Tab) => {
|
||||
if (tab.isActive()) {
|
||||
tab.onActivate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected _getOfferForCollection(offers: DataModels.Offer[], collection: DataModels.Collection): DataModels.Offer {
|
||||
return _.find(offers, (offer: DataModels.Offer) => offer.resource.indexOf(collection._rid) >= 0);
|
||||
}
|
||||
|
|
|
@ -49,12 +49,12 @@ export default class Database implements ViewModels.Database {
|
|||
dataExplorerArea: Constants.Areas.ResourceTree
|
||||
});
|
||||
|
||||
// create settings tab if not created yet
|
||||
const openedTabs = this.container.openedTabs();
|
||||
let settingsTab: ViewModels.Tab = openedTabs
|
||||
.filter(tab => tab.rid === this.rid)
|
||||
.filter(tab => tab.tabKind === ViewModels.CollectionTabKind.DatabaseSettings)[0];
|
||||
const pendingNotificationsPromise: Q.Promise<DataModels.Notification> = this._getPendingThroughputSplitNotification();
|
||||
const matchingTabs: ViewModels.Tab[] = this.container.tabsManager.getTabs(
|
||||
ViewModels.CollectionTabKind.DatabaseSettings,
|
||||
(tab: ViewModels.Tab) => tab.rid === this.rid
|
||||
);
|
||||
let settingsTab: DatabaseSettingsTab = matchingTabs && (matchingTabs[0] as DatabaseSettingsTab);
|
||||
if (!settingsTab) {
|
||||
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
||||
databaseAccountName: this.container.databaseAccount().name,
|
||||
|
@ -78,12 +78,11 @@ export default class Database implements ViewModels.Database {
|
|||
selfLink: this.self,
|
||||
isActive: ko.observable(false),
|
||||
onLoadStartKey: startKey,
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
||||
openedTabs: this.container.openedTabs()
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||
});
|
||||
(settingsTab as ViewModels.DatabaseSettingsTab).pendingNotification(pendingNotification);
|
||||
this.container.openedTabs.push(settingsTab);
|
||||
settingsTab.onTabClick(); // Activate
|
||||
|
||||
settingsTab.pendingNotification(pendingNotification);
|
||||
this.container.tabsManager.activateNewTab(settingsTab);
|
||||
},
|
||||
(error: any) => {
|
||||
TelemetryProcessor.traceFailure(
|
||||
|
@ -109,12 +108,12 @@ export default class Database implements ViewModels.Database {
|
|||
} else {
|
||||
pendingNotificationsPromise.then(
|
||||
(pendingNotification: DataModels.Notification) => {
|
||||
(settingsTab as ViewModels.DatabaseSettingsTab).pendingNotification(pendingNotification);
|
||||
settingsTab.onTabClick();
|
||||
settingsTab.pendingNotification(pendingNotification);
|
||||
this.container.tabsManager.activateTab(settingsTab);
|
||||
},
|
||||
(error: any) => {
|
||||
(settingsTab as ViewModels.DatabaseSettingsTab).pendingNotification(undefined);
|
||||
settingsTab.onTabClick();
|
||||
settingsTab.pendingNotification(undefined);
|
||||
this.container.tabsManager.activateTab(settingsTab);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -221,7 +220,9 @@ export default class Database implements ViewModels.Database {
|
|||
this.expandDatabase();
|
||||
}
|
||||
this.container.onUpdateTabsButtons([]);
|
||||
this.refreshTabSelectedState();
|
||||
this.container.tabsManager.refreshActiveTab(
|
||||
(tab: ViewModels.Tab) => tab.collection && tab.collection.getDatabase().rid === this.rid
|
||||
);
|
||||
}
|
||||
|
||||
public expandDatabase() {
|
||||
|
@ -286,18 +287,6 @@ export default class Database implements ViewModels.Database {
|
|||
database.container.addCollectionPane.open();
|
||||
}
|
||||
|
||||
public refreshTabSelectedState(): void {
|
||||
const openedRelevantTabs: ViewModels.Tab[] = this.container
|
||||
.openedTabs()
|
||||
.filter((tab: ViewModels.Tab) => tab && tab.collection && tab.collection.getDatabase().rid === this.rid);
|
||||
|
||||
openedRelevantTabs.forEach((tab: ViewModels.Tab) => {
|
||||
if (tab.isActive()) {
|
||||
tab.onTabClick(); // this ensures the next (deepest) item in the resource tree is highlighted
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public findCollectionWithId(collectionId: string): ViewModels.Collection {
|
||||
return _.find(this.collections(), (collection: ViewModels.Collection) => collection.id() === collectionId);
|
||||
}
|
||||
|
|
|
@ -73,24 +73,9 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
|
|||
});
|
||||
}
|
||||
|
||||
public refreshActiveTab(): void {
|
||||
// ensures that the tab selects/highlights the right node based on resource tree expand/collapse state
|
||||
const openedRelevantTabs: ViewModels.Tab[] = this.container
|
||||
.openedTabs()
|
||||
.filter((tab: ViewModels.Tab) => tab && tab.collection && tab.collection.rid === this.rid);
|
||||
|
||||
openedRelevantTabs.forEach((tab: ViewModels.Tab) => {
|
||||
if (tab.isActive()) {
|
||||
tab.onActivate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public onNewQueryClick(source: any, event: MouseEvent, queryText?: string) {
|
||||
const collection: ViewModels.Collection = source.collection || source;
|
||||
const explorer: ViewModels.Explorer = source.container;
|
||||
const openedTabs = explorer.openedTabs();
|
||||
const id = openedTabs.filter(t => t.tabKind === ViewModels.CollectionTabKind.Query).length + 1;
|
||||
const id = this.container.tabsManager.getTabs(ViewModels.CollectionTabKind.Query).length + 1;
|
||||
const title = "Query " + id;
|
||||
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
||||
databaseAccountName: this.container.databaseAccount().name,
|
||||
|
@ -101,7 +86,7 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
|
|||
tabTitle: title
|
||||
});
|
||||
|
||||
let queryTab: ViewModels.Tab = new QueryTab({
|
||||
const queryTab: QueryTab = new QueryTab({
|
||||
tabKind: ViewModels.CollectionTabKind.Query,
|
||||
title: title,
|
||||
tabPath: "",
|
||||
|
@ -115,13 +100,10 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
|
|||
partitionKey: collection.partitionKey,
|
||||
resourceTokenPartitionKey: this.container.resourceTokenPartitionKey(),
|
||||
onLoadStartKey: startKey,
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
||||
openedTabs: this.container.openedTabs()
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||
});
|
||||
this.container.openedTabs.push(queryTab);
|
||||
|
||||
// Activate
|
||||
queryTab.onTabClick();
|
||||
this.container.tabsManager.activateNewTab(queryTab);
|
||||
}
|
||||
|
||||
public onDocumentDBDocumentsClick() {
|
||||
|
@ -136,14 +118,15 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
|
|||
dataExplorerArea: Constants.Areas.ResourceTree
|
||||
});
|
||||
|
||||
// create documents tab if not created yet
|
||||
const openedTabs = this.container.openedTabs();
|
||||
const documentsTabs: DocumentsTab[] = this.container.tabsManager.getTabs(
|
||||
ViewModels.CollectionTabKind.Documents,
|
||||
(tab: ViewModels.Tab) => tab.collection && tab.collection.rid === this.rid
|
||||
) as DocumentsTab[];
|
||||
let documentsTab: DocumentsTab = documentsTabs && documentsTabs[0];
|
||||
|
||||
let documentsTab: ViewModels.Tab = openedTabs
|
||||
.filter(tab => tab.collection && tab.collection.rid === this.rid)
|
||||
.filter(tab => tab.tabKind === ViewModels.CollectionTabKind.Documents)[0];
|
||||
|
||||
if (!documentsTab) {
|
||||
if (documentsTab) {
|
||||
this.container.tabsManager.activateTab(documentsTab);
|
||||
} else {
|
||||
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
||||
databaseAccountName: this.container.databaseAccount() && this.container.databaseAccount().name,
|
||||
databaseName: this.databaseId,
|
||||
|
@ -152,6 +135,7 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
|
|||
dataExplorerArea: Constants.Areas.Tab,
|
||||
tabTitle: "Items"
|
||||
});
|
||||
|
||||
documentsTab = new DocumentsTab({
|
||||
partitionKey: this.partitionKey,
|
||||
resourceTokenPartitionKey: this.container.resourceTokenPartitionKey(),
|
||||
|
@ -167,14 +151,11 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
|
|||
tabPath: `${this.databaseId}>${this.id()}>Documents`,
|
||||
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/documents`,
|
||||
onLoadStartKey: startKey,
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
||||
openedTabs: this.container.openedTabs()
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||
});
|
||||
this.container.openedTabs.push(documentsTab);
|
||||
}
|
||||
|
||||
// Activate
|
||||
documentsTab.onTabClick();
|
||||
this.container.tabsManager.activateNewTab(documentsTab);
|
||||
}
|
||||
}
|
||||
|
||||
public getDatabase(): ViewModels.Database {
|
||||
|
|
|
@ -46,7 +46,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
this.parameters = ko.observable(Date.now());
|
||||
|
||||
this.container.selectedNode.subscribe((newValue: any) => this.triggerRender());
|
||||
this.container.activeTab.subscribe((newValue: ViewModels.Tab) => this.triggerRender());
|
||||
this.container.tabsManager.activeTab.subscribe((newValue: ViewModels.Tab) => this.triggerRender());
|
||||
this.container.isNotebookEnabled.subscribe(newValue => this.triggerRender());
|
||||
|
||||
this.koSubsDatabaseIdMap = new ArrayHashMap();
|
||||
|
@ -171,7 +171,9 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
}
|
||||
database.selectDatabase();
|
||||
this.container.onUpdateTabsButtons([]);
|
||||
database.refreshTabSelectedState();
|
||||
this.container.tabsManager.refreshActiveTab(
|
||||
(tab: ViewModels.Tab) => tab.collection && tab.collection.getDatabase().rid === database.rid
|
||||
);
|
||||
},
|
||||
onContextMenuOpen: () => this.container.selectedNode(database)
|
||||
};
|
||||
|
@ -268,7 +270,9 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
// Rewritten version of expandCollapseCollection
|
||||
this.container.selectedNode(collection);
|
||||
this.container.onUpdateTabsButtons([]);
|
||||
collection.refreshActiveTab();
|
||||
this.container.tabsManager.refreshActiveTab(
|
||||
(tab: ViewModels.Tab) => tab.collection && tab.collection.rid === collection.rid
|
||||
);
|
||||
},
|
||||
onExpanded: () => {
|
||||
if (ResourceTreeAdapter.showScriptNodes(this.container)) {
|
||||
|
@ -294,7 +298,9 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
})),
|
||||
onClick: () => {
|
||||
collection.selectedSubnodeKind(ViewModels.CollectionTabKind.StoredProcedures);
|
||||
collection.refreshActiveTab();
|
||||
this.container.tabsManager.refreshActiveTab(
|
||||
(tab: ViewModels.Tab) => tab.collection && tab.collection.rid === collection.rid
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -311,7 +317,9 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
})),
|
||||
onClick: () => {
|
||||
collection.selectedSubnodeKind(ViewModels.CollectionTabKind.UserDefinedFunctions);
|
||||
collection.refreshActiveTab();
|
||||
this.container.tabsManager.refreshActiveTab(
|
||||
(tab: ViewModels.Tab) => tab.collection && tab.collection.rid === collection.rid
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -327,7 +335,9 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
})),
|
||||
onClick: () => {
|
||||
collection.selectedSubnodeKind(ViewModels.CollectionTabKind.Triggers);
|
||||
collection.refreshActiveTab();
|
||||
this.container.tabsManager.refreshActiveTab(
|
||||
(tab: ViewModels.Tab) => tab.collection && tab.collection.rid === collection.rid
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -409,7 +419,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
className: "notebookHeader galleryHeader",
|
||||
onClick: () => this.container.openGallery(),
|
||||
isSelected: () => {
|
||||
const activeTab = this.container.findActiveTab();
|
||||
const activeTab = this.container.tabsManager.activeTab();
|
||||
return activeTab && activeTab.tabKind === ViewModels.CollectionTabKind.Gallery;
|
||||
}
|
||||
};
|
||||
|
@ -517,7 +527,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
className: "notebookHeader",
|
||||
onClick: () => onFileClick(item),
|
||||
isSelected: () => {
|
||||
const activeTab = this.container.findActiveTab();
|
||||
const activeTab = this.container.tabsManager.activeTab();
|
||||
return (
|
||||
activeTab &&
|
||||
activeTab.tabKind === ViewModels.CollectionTabKind.NotebookV2 &&
|
||||
|
@ -634,7 +644,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
}
|
||||
},
|
||||
isSelected: () => {
|
||||
const activeTab = this.container.findActiveTab();
|
||||
const activeTab = this.container.tabsManager.activeTab();
|
||||
return (
|
||||
activeTab &&
|
||||
activeTab.tabKind === ViewModels.CollectionTabKind.NotebookV2 &&
|
||||
|
@ -657,14 +667,6 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
window.requestAnimationFrame(() => this.parameters(Date.now()));
|
||||
}
|
||||
|
||||
private getActiveTab(): ViewModels.Tab {
|
||||
const activeTabs: ViewModels.Tab[] = this.container.openedTabs().filter((tab: ViewModels.Tab) => tab.isActive());
|
||||
if (activeTabs.length) {
|
||||
return activeTabs[0];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private isDataNodeSelected(rid: string, nodeKind: string, subnodeKind: ViewModels.CollectionTabKind): boolean {
|
||||
if (!this.container.selectedNode || !this.container.selectedNode()) {
|
||||
return false;
|
||||
|
@ -674,7 +676,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||
if (subnodeKind === undefined) {
|
||||
return selectedNode.rid === rid && selectedNode.nodeKind === nodeKind;
|
||||
} else {
|
||||
const activeTab = this.getActiveTab();
|
||||
const activeTab = this.container.tabsManager.activeTab();
|
||||
let selectedSubnodeKind;
|
||||
if (nodeKind === "Database" && (selectedNode as ViewModels.Database).selectedSubnodeKind) {
|
||||
selectedSubnodeKind = (selectedNode as ViewModels.Database).selectedSubnodeKind();
|
||||
|
|
|
@ -12,9 +12,7 @@ const createMockContainer = (): ViewModels.Explorer => {
|
|||
let mockContainer = {} as ViewModels.Explorer;
|
||||
mockContainer.resourceTokenCollection = createMockCollection(mockContainer);
|
||||
mockContainer.selectedNode = ko.observable<ViewModels.TreeNode>();
|
||||
mockContainer.activeTab = ko.observable<ViewModels.Tab>();
|
||||
mockContainer.mostRecentActivity = new MostRecentActivity.MostRecentActivity(mockContainer);
|
||||
mockContainer.openedTabs = ko.observableArray<ViewModels.Tab>([]);
|
||||
mockContainer.onUpdateTabsButtons = () => {};
|
||||
|
||||
return mockContainer;
|
||||
|
|
|
@ -17,7 +17,8 @@ export class ResourceTreeAdapterForResourceToken implements ReactAdapter {
|
|||
|
||||
this.container.resourceTokenCollection.subscribe((collection: ViewModels.CollectionBase) => this.triggerRender());
|
||||
this.container.selectedNode.subscribe((newValue: any) => this.triggerRender());
|
||||
this.container.activeTab.subscribe((newValue: ViewModels.Tab) => this.triggerRender());
|
||||
this.container.tabsManager &&
|
||||
this.container.tabsManager.activeTab.subscribe((newValue: ViewModels.Tab) => this.triggerRender());
|
||||
|
||||
this.triggerRender();
|
||||
}
|
||||
|
@ -63,7 +64,9 @@ export class ResourceTreeAdapterForResourceToken implements ReactAdapter {
|
|||
// Rewritten version of expandCollapseCollection
|
||||
this.container.selectedNode(collection);
|
||||
this.container.onUpdateTabsButtons([]);
|
||||
collection.refreshActiveTab();
|
||||
this.container.tabsManager.refreshActiveTab(
|
||||
(tab: ViewModels.Tab) => tab.collection && tab.collection.rid === collection.rid
|
||||
);
|
||||
},
|
||||
isSelected: () => this.isDataNodeSelected(collection.rid, "Collection", undefined)
|
||||
};
|
||||
|
@ -75,14 +78,6 @@ export class ResourceTreeAdapterForResourceToken implements ReactAdapter {
|
|||
};
|
||||
}
|
||||
|
||||
private getActiveTab(): ViewModels.Tab {
|
||||
const activeTabs: ViewModels.Tab[] = this.container.openedTabs().filter((tab: ViewModels.Tab) => tab.isActive());
|
||||
if (activeTabs.length) {
|
||||
return activeTabs[0];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private isDataNodeSelected(rid: string, nodeKind: string, subnodeKind: ViewModels.CollectionTabKind): boolean {
|
||||
if (!this.container.selectedNode || !this.container.selectedNode()) {
|
||||
return false;
|
||||
|
@ -92,7 +87,7 @@ export class ResourceTreeAdapterForResourceToken implements ReactAdapter {
|
|||
if (subnodeKind) {
|
||||
return selectedNode.rid === rid && selectedNode.nodeKind === nodeKind;
|
||||
} else {
|
||||
const activeTab = this.getActiveTab();
|
||||
const activeTab = this.container.tabsManager.activeTab();
|
||||
let selectedSubnodeKind;
|
||||
if (nodeKind === "Database" && (selectedNode as ViewModels.Database).selectedSubnodeKind) {
|
||||
selectedSubnodeKind = (selectedNode as ViewModels.Database).selectedSubnodeKind();
|
||||
|
|
|
@ -56,15 +56,13 @@ export default class StoredProcedure implements ViewModels.StoredProcedure {
|
|||
}
|
||||
|
||||
public static create(source: ViewModels.Collection, event: MouseEvent) {
|
||||
const openedTabs = source.container.openedTabs();
|
||||
const id =
|
||||
openedTabs.filter((tab: ViewModels.Tab) => tab.tabKind === ViewModels.CollectionTabKind.StoredProcedures).length +
|
||||
1;
|
||||
const id = source.container.tabsManager.getTabs(ViewModels.CollectionTabKind.StoredProcedures).length + 1;
|
||||
const storedProcedure = <DataModels.StoredProcedure>{
|
||||
id: "",
|
||||
body: sampleStoredProcedureBody
|
||||
};
|
||||
let storedProcedureTab: ViewModels.Tab = new StoredProcedureTab({
|
||||
|
||||
const storedProcedureTab: StoredProcedureTab = new StoredProcedureTab({
|
||||
resource: storedProcedure,
|
||||
isNew: true,
|
||||
tabKind: ViewModels.CollectionTabKind.StoredProcedures,
|
||||
|
@ -76,13 +74,10 @@ export default class StoredProcedure implements ViewModels.StoredProcedure {
|
|||
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(source.databaseId, source.id())}/sproc`,
|
||||
selfLink: "",
|
||||
isActive: ko.observable(false),
|
||||
onUpdateTabsButtons: source.container.onUpdateTabsButtons,
|
||||
openedTabs: source.container.openedTabs()
|
||||
onUpdateTabsButtons: source.container.onUpdateTabsButtons
|
||||
});
|
||||
source.container.openedTabs.push(storedProcedureTab);
|
||||
|
||||
// Activate
|
||||
storedProcedureTab.onTabClick();
|
||||
source.container.tabsManager.activateNewTab(storedProcedureTab);
|
||||
}
|
||||
|
||||
public select() {
|
||||
|
@ -98,15 +93,15 @@ export default class StoredProcedure implements ViewModels.StoredProcedure {
|
|||
public open = () => {
|
||||
this.select();
|
||||
|
||||
const openedTabs = this.container.openedTabs();
|
||||
const storedProcedureTabsOpen: ViewModels.Tab[] =
|
||||
openedTabs &&
|
||||
openedTabs.filter(
|
||||
tab => tab.node && tab.node.rid === this.rid && tab.tabKind === ViewModels.CollectionTabKind.StoredProcedures
|
||||
);
|
||||
let storedProcedureTab: ViewModels.Tab =
|
||||
storedProcedureTabsOpen && storedProcedureTabsOpen.length > 0 && storedProcedureTabsOpen[0];
|
||||
if (!storedProcedureTab) {
|
||||
const storedProcedureTabs: StoredProcedureTab[] = this.container.tabsManager.getTabs(
|
||||
ViewModels.CollectionTabKind.StoredProcedures,
|
||||
(tab: ViewModels.Tab) => tab.node && tab.node.rid === this.rid
|
||||
) as StoredProcedureTab[];
|
||||
let storedProcedureTab: StoredProcedureTab = storedProcedureTabs && storedProcedureTabs[0];
|
||||
|
||||
if (storedProcedureTab) {
|
||||
this.container.tabsManager.activateTab(storedProcedureTab);
|
||||
} else {
|
||||
const storedProcedureData = <DataModels.StoredProcedure>{
|
||||
_rid: this.rid,
|
||||
_self: this.self,
|
||||
|
@ -129,14 +124,11 @@ export default class StoredProcedure implements ViewModels.StoredProcedure {
|
|||
)}/sprocs/${this.id()}`,
|
||||
selfLink: this.self,
|
||||
isActive: ko.observable(false),
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
||||
openedTabs: this.container.openedTabs()
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||
});
|
||||
this.container.openedTabs.push(storedProcedureTab);
|
||||
}
|
||||
|
||||
// Activate
|
||||
storedProcedureTab.onTabClick();
|
||||
this.container.tabsManager.activateNewTab(storedProcedureTab);
|
||||
}
|
||||
};
|
||||
|
||||
public delete() {
|
||||
|
@ -153,7 +145,9 @@ export default class StoredProcedure implements ViewModels.StoredProcedure {
|
|||
|
||||
this.container.documentClientUtility.deleteStoredProcedure(this.collection, storedProcedureData).then(
|
||||
() => {
|
||||
this.container.openedTabs.remove((tab: ViewModels.Tab) => tab.node && tab.node.rid === this.rid);
|
||||
this.container.tabsManager.removeTabByComparator(
|
||||
(tab: ViewModels.Tab) => tab.node && tab.node.rid === this.rid
|
||||
);
|
||||
this.collection.children.remove(this);
|
||||
},
|
||||
reason => {}
|
||||
|
@ -161,7 +155,11 @@ export default class StoredProcedure implements ViewModels.StoredProcedure {
|
|||
}
|
||||
|
||||
public execute(params: string[], partitionKeyValue?: string): void {
|
||||
const sprocTab: ViewModels.StoredProcedureTab = this._getCurrentStoredProcedureTab();
|
||||
const sprocTabs: ViewModels.StoredProcedureTab[] = this.container.tabsManager.getTabs(
|
||||
ViewModels.CollectionTabKind.StoredProcedures,
|
||||
(tab: ViewModels.Tab) => tab.node && tab.node.rid === this.rid
|
||||
) as ViewModels.StoredProcedureTab[];
|
||||
const sprocTab: ViewModels.StoredProcedureTab = sprocTabs && sprocTabs.length > 0 && sprocTabs[0];
|
||||
sprocTab.isExecuting(true);
|
||||
this.container &&
|
||||
this.container.documentClientUtility
|
||||
|
@ -184,17 +182,4 @@ export default class StoredProcedure implements ViewModels.StoredProcedure {
|
|||
const focusElement = document.getElementById("execute-storedproc-toggles");
|
||||
focusElement && focusElement.focus();
|
||||
}
|
||||
|
||||
private _getCurrentStoredProcedureTab(): ViewModels.StoredProcedureTab {
|
||||
const openedTabs = this.container.openedTabs();
|
||||
const storedProcedureTabsOpen: ViewModels.Tab[] =
|
||||
openedTabs &&
|
||||
openedTabs.filter(
|
||||
tab => tab.node && tab.node.rid === this.rid && tab.tabKind === ViewModels.CollectionTabKind.StoredProcedures
|
||||
);
|
||||
|
||||
return (storedProcedureTabsOpen &&
|
||||
storedProcedureTabsOpen.length > 0 &&
|
||||
storedProcedureTabsOpen[0]) as ViewModels.StoredProcedureTab;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ import * as ViewModels from "../../Contracts/ViewModels";
|
|||
import * as Constants from "../../Common/Constants";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import Collection from "./Collection";
|
||||
import TriggerTab from "../Tabs/TriggerTab";
|
||||
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
|
||||
|
@ -41,10 +40,7 @@ export default class Trigger implements ViewModels.Trigger {
|
|||
}
|
||||
|
||||
public static create(source: ViewModels.Collection, event: MouseEvent) {
|
||||
const id =
|
||||
source.container
|
||||
.openedTabs()
|
||||
.filter((tab: ViewModels.Tab) => tab.tabKind === ViewModels.CollectionTabKind.Triggers).length + 1;
|
||||
const id = source.container.tabsManager.getTabs(ViewModels.CollectionTabKind.Triggers).length + 1;
|
||||
const trigger = <DataModels.Trigger>{
|
||||
id: "",
|
||||
body: "function trigger(){}",
|
||||
|
@ -52,7 +48,7 @@ export default class Trigger implements ViewModels.Trigger {
|
|||
triggerType: "Pre"
|
||||
};
|
||||
|
||||
let triggerTab: ViewModels.Tab = new TriggerTab({
|
||||
const triggerTab: TriggerTab = new TriggerTab({
|
||||
resource: trigger,
|
||||
isNew: true,
|
||||
tabKind: ViewModels.CollectionTabKind.Triggers,
|
||||
|
@ -64,23 +60,24 @@ export default class Trigger implements ViewModels.Trigger {
|
|||
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(source.databaseId, source.id())}/trigger`,
|
||||
selfLink: "",
|
||||
isActive: ko.observable(false),
|
||||
onUpdateTabsButtons: source.container.onUpdateTabsButtons,
|
||||
openedTabs: source.container.openedTabs()
|
||||
onUpdateTabsButtons: source.container.onUpdateTabsButtons
|
||||
});
|
||||
|
||||
source.container.openedTabs.push(triggerTab);
|
||||
|
||||
// Activate
|
||||
triggerTab.onTabClick();
|
||||
source.container.tabsManager.activateNewTab(triggerTab);
|
||||
}
|
||||
|
||||
public open = () => {
|
||||
this.select();
|
||||
|
||||
let triggerTab: ViewModels.Tab = this.container
|
||||
.openedTabs()
|
||||
.filter(tab => tab.node && tab.node.rid === this.rid)[0];
|
||||
if (!triggerTab) {
|
||||
const triggerTabs: TriggerTab[] = this.container.tabsManager.getTabs(
|
||||
ViewModels.CollectionTabKind.Triggers,
|
||||
(tab: ViewModels.Tab) => tab.node && tab.node.rid === this.rid
|
||||
) as TriggerTab[];
|
||||
let triggerTab: TriggerTab = triggerTabs && triggerTabs[0];
|
||||
|
||||
if (triggerTab) {
|
||||
this.container.tabsManager.activateTab(triggerTab);
|
||||
} else {
|
||||
const triggerData = <DataModels.Trigger>{
|
||||
_rid: this.rid,
|
||||
_self: this.self,
|
||||
|
@ -105,15 +102,11 @@ export default class Trigger implements ViewModels.Trigger {
|
|||
)}/triggers/${this.id()}`,
|
||||
selfLink: "",
|
||||
isActive: ko.observable(false),
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
||||
openedTabs: this.container.openedTabs()
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||
});
|
||||
|
||||
this.container.openedTabs.push(triggerTab);
|
||||
this.container.tabsManager.activateNewTab(triggerTab);
|
||||
}
|
||||
|
||||
// Activate
|
||||
triggerTab.onTabClick();
|
||||
};
|
||||
|
||||
public delete() {
|
||||
|
@ -132,7 +125,9 @@ export default class Trigger implements ViewModels.Trigger {
|
|||
|
||||
this.container.documentClientUtility.deleteTrigger(this.collection, triggerData).then(
|
||||
() => {
|
||||
this.container.openedTabs.remove((tab: ViewModels.Tab) => tab.node && tab.node.rid === this.rid);
|
||||
this.container.tabsManager.removeTabByComparator(
|
||||
(tab: ViewModels.Tab) => tab.node && tab.node.rid === this.rid
|
||||
);
|
||||
this.collection.children.remove(this);
|
||||
},
|
||||
reason => {}
|
||||
|
|
|
@ -5,7 +5,6 @@ import * as DataModels from "../../Contracts/DataModels";
|
|||
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import UserDefinedFunctionTab from "../Tabs/UserDefinedFunctionTab";
|
||||
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import Collection from "./Collection";
|
||||
|
||||
export default class UserDefinedFunction implements ViewModels.UserDefinedFunction {
|
||||
public nodeKind: string;
|
||||
|
@ -28,15 +27,13 @@ export default class UserDefinedFunction implements ViewModels.UserDefinedFuncti
|
|||
}
|
||||
|
||||
public static create(source: ViewModels.Collection, event: MouseEvent) {
|
||||
const id =
|
||||
source.container
|
||||
.openedTabs()
|
||||
.filter((tab: ViewModels.Tab) => tab.tabKind === ViewModels.CollectionTabKind.UserDefinedFunctions).length + 1;
|
||||
const id = source.container.tabsManager.getTabs(ViewModels.CollectionTabKind.UserDefinedFunctions).length + 1;
|
||||
const userDefinedFunction = <DataModels.UserDefinedFunction>{
|
||||
id: "",
|
||||
body: "function userDefinedFunction(){}"
|
||||
};
|
||||
let userDefinedFunctionTab: ViewModels.Tab = new UserDefinedFunctionTab({
|
||||
|
||||
const userDefinedFunctionTab: UserDefinedFunctionTab = new UserDefinedFunctionTab({
|
||||
resource: userDefinedFunction,
|
||||
isNew: true,
|
||||
tabKind: ViewModels.CollectionTabKind.UserDefinedFunctions,
|
||||
|
@ -48,22 +45,24 @@ export default class UserDefinedFunction implements ViewModels.UserDefinedFuncti
|
|||
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(source.databaseId, source.id())}/udf`,
|
||||
selfLink: "",
|
||||
isActive: ko.observable(false),
|
||||
onUpdateTabsButtons: source.container.onUpdateTabsButtons,
|
||||
openedTabs: source.container.openedTabs()
|
||||
onUpdateTabsButtons: source.container.onUpdateTabsButtons
|
||||
});
|
||||
source.container.openedTabs.push(userDefinedFunctionTab);
|
||||
|
||||
// Activate
|
||||
userDefinedFunctionTab.onTabClick();
|
||||
source.container.tabsManager.activateNewTab(userDefinedFunctionTab);
|
||||
}
|
||||
|
||||
public open = () => {
|
||||
this.select();
|
||||
|
||||
let userDefinedFunctionTab: ViewModels.Tab = this.container
|
||||
.openedTabs()
|
||||
.filter(tab => tab.node && tab.node.rid === this.rid)[0];
|
||||
if (!userDefinedFunctionTab) {
|
||||
const userDefinedFunctionTabs: UserDefinedFunctionTab[] = this.container.tabsManager.getTabs(
|
||||
ViewModels.CollectionTabKind.UserDefinedFunctions,
|
||||
(tab: ViewModels.Tab) => tab.collection && tab.collection.rid === this.rid
|
||||
) as UserDefinedFunctionTab[];
|
||||
let userDefinedFunctionTab: UserDefinedFunctionTab = userDefinedFunctionTabs && userDefinedFunctionTabs[0];
|
||||
|
||||
if (userDefinedFunctionTab) {
|
||||
this.container.tabsManager.activateTab(userDefinedFunctionTab);
|
||||
} else {
|
||||
const userDefinedFunctionData = <DataModels.UserDefinedFunction>{
|
||||
_rid: this.rid,
|
||||
_self: this.self,
|
||||
|
@ -86,14 +85,11 @@ export default class UserDefinedFunction implements ViewModels.UserDefinedFuncti
|
|||
)}/udfs/${this.id()}`,
|
||||
selfLink: "",
|
||||
isActive: ko.observable(false),
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
||||
openedTabs: this.container.openedTabs()
|
||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||
});
|
||||
this.container.openedTabs.push(userDefinedFunctionTab);
|
||||
}
|
||||
|
||||
// Activate
|
||||
userDefinedFunctionTab.onTabClick();
|
||||
this.container.tabsManager.activateNewTab(userDefinedFunctionTab);
|
||||
}
|
||||
};
|
||||
|
||||
public select() {
|
||||
|
@ -119,7 +115,9 @@ export default class UserDefinedFunction implements ViewModels.UserDefinedFuncti
|
|||
};
|
||||
this.container.documentClientUtility.deleteUserDefinedFunction(this.collection, userDefinedFunctionData).then(
|
||||
() => {
|
||||
this.container.openedTabs.remove((tab: ViewModels.Tab) => tab.node && tab.node.rid === this.rid);
|
||||
this.container.tabsManager.removeTabByComparator(
|
||||
(tab: ViewModels.Tab) => tab.node && tab.node.rid === this.rid
|
||||
);
|
||||
this.collection.children.remove(this);
|
||||
},
|
||||
reason => {}
|
||||
|
|
|
@ -390,17 +390,15 @@ export class TabRouteHandler {
|
|||
isNewScriptTab?: boolean
|
||||
): ViewModels.Tab {
|
||||
const explorer: ViewModels.Explorer = (<any>window).dataExplorer;
|
||||
|
||||
return _.find(explorer.openedTabs(), (tab: ViewModels.Tab) => {
|
||||
const isMatchingTabKind =
|
||||
tab.collection.databaseId === databaseId && tab.collection.id() === collectionId && tab.tabKind === tabKind;
|
||||
|
||||
if (isNewScriptTab) {
|
||||
return isMatchingTabKind && (tab as ViewModels.ScriptTab).isNew();
|
||||
}
|
||||
|
||||
return isMatchingTabKind;
|
||||
});
|
||||
const matchingTabs: ViewModels.Tab[] = explorer.tabsManager.getTabs(
|
||||
tabKind,
|
||||
(tab: ViewModels.Tab) =>
|
||||
tab.collection &&
|
||||
tab.collection.databaseId === databaseId &&
|
||||
tab.collection.id() === collectionId &&
|
||||
(!isNewScriptTab || (tab as ViewModels.ScriptTab).isNew())
|
||||
);
|
||||
return matchingTabs && matchingTabs[0];
|
||||
}
|
||||
|
||||
private _findMatchingCollectionForResource(databaseId: string, collectionId: string): ViewModels.Collection {
|
||||
|
|
|
@ -187,171 +187,13 @@
|
|||
|
||||
<div
|
||||
class="connectExplorerContainer"
|
||||
data-bind="visible: !isRefreshingExplorer() && (openedTabs == null || openedTabs().length === 0)"
|
||||
data-bind="visible: !isRefreshingExplorer() && tabsManager.openedTabs().length === 0"
|
||||
>
|
||||
<form class="connectExplorerFormContainer">
|
||||
<div class="connectExplorer" data-bind="react: splashScreenAdapter"></div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Tabs and Tabs Panes - Start -->
|
||||
<div
|
||||
id="content"
|
||||
class="flexContainer hideOverflows"
|
||||
data-bind="
|
||||
visible: openedTabs && openedTabs().length > 0"
|
||||
>
|
||||
<!-- Tabs - Start -->
|
||||
<div class="nav-tabs-margin" data-bind="visible:!isTabsContentExpanded()">
|
||||
<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>
|
||||
<!-- Tabs and Tabs Panes - End -->
|
||||
<tabs-manager class="tabsContainer" params="{data: tabsManager}"></tabs-manager>
|
||||
</div>
|
||||
<!-- Collections Tree and Tabs - End -->
|
||||
<div
|
||||
|
|
Loading…
Reference in New Issue