mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2024-11-25 06:56:38 +00:00
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;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tabsContainer {
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.tabs {
|
.tabs {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 15px 0 25px 0;
|
margin: 15px 0 25px 0;
|
||||||
|
2808
package-lock.json
generated
2808
package-lock.json
generated
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 { SetupNotebooksPane } from "../Explorer/Panes/SetupNotebooksPane";
|
||||||
import { Splitter } from "../Common/Splitter";
|
import { Splitter } from "../Common/Splitter";
|
||||||
import { StringInputPane } from "../Explorer/Panes/StringInputPane";
|
import { StringInputPane } from "../Explorer/Panes/StringInputPane";
|
||||||
|
import { TabsManager } from "../Explorer/Tabs/TabsManager";
|
||||||
import { TextFieldProps } from "../Explorer/Controls/DialogReactComponent/DialogComponent";
|
import { TextFieldProps } from "../Explorer/Controls/DialogReactComponent/DialogComponent";
|
||||||
import { UploadDetails } from "../workers/upload/definitions";
|
import { UploadDetails } from "../workers/upload/definitions";
|
||||||
import { UploadItemsPaneAdapter } from "../Explorer/Panes/UploadItemsPaneAdapter";
|
import { UploadItemsPaneAdapter } from "../Explorer/Panes/UploadItemsPaneAdapter";
|
||||||
@ -121,9 +122,8 @@ export interface Explorer {
|
|||||||
isResourceTokenCollectionNodeSelected: ko.Computed<boolean>;
|
isResourceTokenCollectionNodeSelected: ko.Computed<boolean>;
|
||||||
|
|
||||||
// Tabs
|
// Tabs
|
||||||
openedTabs: ko.ObservableArray<Tab>;
|
|
||||||
activeTab: ko.Observable<Tab>;
|
|
||||||
isTabsContentExpanded: ko.Observable<boolean>;
|
isTabsContentExpanded: ko.Observable<boolean>;
|
||||||
|
tabsManager: TabsManager;
|
||||||
|
|
||||||
// Contextual Panes
|
// Contextual Panes
|
||||||
addDatabasePane: AddDatabasePane;
|
addDatabasePane: AddDatabasePane;
|
||||||
@ -161,8 +161,6 @@ export interface Explorer {
|
|||||||
refreshDatabaseForResourceToken(): Q.Promise<void>;
|
refreshDatabaseForResourceToken(): Q.Promise<void>;
|
||||||
refreshAllDatabases(isInitialLoad?: boolean): Q.Promise<any>;
|
refreshAllDatabases(isInitialLoad?: boolean): Q.Promise<any>;
|
||||||
closeAllPanes(): void;
|
closeAllPanes(): void;
|
||||||
closeAllTabsForResource(resourceId: string): void;
|
|
||||||
findActiveTab(): Tab; // TODO Deprecate in favor activeTab
|
|
||||||
findSelectedDatabase(): Database;
|
findSelectedDatabase(): Database;
|
||||||
findDatabaseWithId(databaseRid: string): Database;
|
findDatabaseWithId(databaseRid: string): Database;
|
||||||
isLastDatabase(): boolean;
|
isLastDatabase(): boolean;
|
||||||
@ -221,7 +219,6 @@ export interface Explorer {
|
|||||||
sparkClusterConnectionInfo: ko.Observable<DataModels.SparkClusterConnectionInfo>;
|
sparkClusterConnectionInfo: ko.Observable<DataModels.SparkClusterConnectionInfo>;
|
||||||
arcadiaToken: ko.Observable<string>;
|
arcadiaToken: ko.Observable<string>;
|
||||||
arcadiaWorkspaces: ko.ObservableArray<ArcadiaWorkspaceItem>;
|
arcadiaWorkspaces: ko.ObservableArray<ArcadiaWorkspaceItem>;
|
||||||
isNotebookTabActive: ko.Computed<boolean>;
|
|
||||||
memoryUsageInfo: ko.Observable<DataModels.MemoryUsageInfo>;
|
memoryUsageInfo: ko.Observable<DataModels.MemoryUsageInfo>;
|
||||||
notebookManager?: any; // This is dynamically loaded
|
notebookManager?: any; // This is dynamically loaded
|
||||||
openNotebook(notebookContentItem: NotebookContentItem): Promise<boolean>; // True if it was opened, false otherwise
|
openNotebook(notebookContentItem: NotebookContentItem): Promise<boolean>; // True if it was opened, false otherwise
|
||||||
@ -250,7 +247,6 @@ export interface Explorer {
|
|||||||
readFile: (notebookFile: NotebookContentItem) => Promise<string>;
|
readFile: (notebookFile: NotebookContentItem) => Promise<string>;
|
||||||
downloadFile: (notebookFile: NotebookContentItem) => Promise<void>;
|
downloadFile: (notebookFile: NotebookContentItem) => Promise<void>;
|
||||||
createNotebookContentItemFile: (name: string, filepath: string) => NotebookContentItem;
|
createNotebookContentItemFile: (name: string, filepath: string) => NotebookContentItem;
|
||||||
closeNotebookTab: (filepath: string) => void;
|
|
||||||
refreshContentItem(item: NotebookContentItem): Promise<void>;
|
refreshContentItem(item: NotebookContentItem): Promise<void>;
|
||||||
getNotebookBasePath(): string;
|
getNotebookBasePath(): string;
|
||||||
|
|
||||||
@ -381,7 +377,6 @@ export interface Database extends TreeNode {
|
|||||||
findCollectionWithId(collectionRid: string): Collection;
|
findCollectionWithId(collectionRid: string): Collection;
|
||||||
openAddCollection(database: Database, event: MouseEvent): void;
|
openAddCollection(database: Database, event: MouseEvent): void;
|
||||||
onDeleteDatabaseContextMenuClick(source: Database, event: MouseEvent | KeyboardEvent): void;
|
onDeleteDatabaseContextMenuClick(source: Database, event: MouseEvent | KeyboardEvent): void;
|
||||||
refreshTabSelectedState(): void;
|
|
||||||
readSettings(): void;
|
readSettings(): void;
|
||||||
onSettingsClick: () => void;
|
onSettingsClick: () => void;
|
||||||
}
|
}
|
||||||
@ -403,7 +398,6 @@ export interface CollectionBase extends TreeNode {
|
|||||||
onNewQueryClick(source: any, event: MouseEvent, queryText?: string): void;
|
onNewQueryClick(source: any, event: MouseEvent, queryText?: string): void;
|
||||||
expandCollection(): Q.Promise<any>;
|
expandCollection(): Q.Promise<any>;
|
||||||
collapseCollection(): void;
|
collapseCollection(): void;
|
||||||
refreshActiveTab(): void;
|
|
||||||
getDatabase(): Database;
|
getDatabase(): Database;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -837,7 +831,6 @@ export interface TabOptions {
|
|||||||
onUpdateTabsButtons: (buttons: NavbarButtonConfig[]) => void;
|
onUpdateTabsButtons: (buttons: NavbarButtonConfig[]) => void;
|
||||||
isTabsContentExpanded?: ko.Observable<boolean>;
|
isTabsContentExpanded?: ko.Observable<boolean>;
|
||||||
onLoadStartKey?: number;
|
onLoadStartKey?: number;
|
||||||
openedTabs: Tab[];
|
|
||||||
|
|
||||||
// TODO Remove the flag and use a context to handle this
|
// TODO Remove the flag and use a context to handle this
|
||||||
// TODO: 145357 Remove dependency on collection/database and add abstraction
|
// TODO: 145357 Remove dependency on collection/database and add abstraction
|
||||||
@ -931,14 +924,12 @@ export interface Tab {
|
|||||||
tabTitle: ko.Observable<string>;
|
tabTitle: ko.Observable<string>;
|
||||||
hashLocation: ko.Observable<string>;
|
hashLocation: ko.Observable<string>;
|
||||||
closeTabButton: Button;
|
closeTabButton: Button;
|
||||||
onCloseTabButtonClick(): Q.Promise<any>;
|
onCloseTabButtonClick(): void;
|
||||||
onTabClick(): Q.Promise<any>;
|
onTabClick(): Q.Promise<any>;
|
||||||
onKeyPressActivate(source: any, event: KeyboardEvent): void;
|
onKeyPressActivate(source: any, event: KeyboardEvent): void;
|
||||||
onKeyPressClose(source: any, event: KeyboardEvent): void;
|
onKeyPressClose(source: any, event: KeyboardEvent): void;
|
||||||
onActivate(): Q.Promise<any>;
|
onActivate(): Q.Promise<any>;
|
||||||
refresh(): void;
|
refresh(): void;
|
||||||
nextTab: ko.Observable<Tab>;
|
|
||||||
previousTab: ko.Observable<Tab>;
|
|
||||||
closeButtonTabIndex: ko.Computed<number>;
|
closeButtonTabIndex: ko.Computed<number>;
|
||||||
isExecutionError: ko.Observable<boolean>;
|
isExecutionError: ko.Observable<boolean>;
|
||||||
isExecuting: ko.Observable<boolean>;
|
isExecuting: ko.Observable<boolean>;
|
||||||
|
@ -10,6 +10,7 @@ import { GraphStyleComponent } from "./Graph/GraphStyleComponent/GraphStyleCompo
|
|||||||
import { InputTypeaheadComponent } from "./Controls/InputTypeahead/InputTypeahead";
|
import { InputTypeaheadComponent } from "./Controls/InputTypeahead/InputTypeahead";
|
||||||
import { JsonEditorComponent } from "./Controls/JsonEditor/JsonEditorComponent";
|
import { JsonEditorComponent } from "./Controls/JsonEditor/JsonEditorComponent";
|
||||||
import { NewVertexComponent } from "./Graph/NewVertexComponent/NewVertexComponent";
|
import { NewVertexComponent } from "./Graph/NewVertexComponent/NewVertexComponent";
|
||||||
|
import { TabsManagerKOComponent } from "./Tabs/TabsManager";
|
||||||
import { ThroughputInputComponent } from "./Controls/ThroughputInput/ThroughputInputComponent";
|
import { ThroughputInputComponent } from "./Controls/ThroughputInput/ThroughputInputComponent";
|
||||||
import { ThroughputInputComponentAutoPilotV3 } from "./Controls/ThroughputInput/ThroughputInputComponentAutoPilotV3";
|
import { ThroughputInputComponentAutoPilotV3 } from "./Controls/ThroughputInput/ThroughputInputComponentAutoPilotV3";
|
||||||
import { ToolbarComponent } from "./Controls/Toolbar/Toolbar";
|
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("dynamic-list", DynamicListComponent);
|
||||||
ko.components.register("throughput-input", ThroughputInputComponent);
|
ko.components.register("throughput-input", ThroughputInputComponent);
|
||||||
ko.components.register("throughput-input-autopilot-v3", ThroughputInputComponentAutoPilotV3);
|
ko.components.register("throughput-input-autopilot-v3", ThroughputInputComponentAutoPilotV3);
|
||||||
|
ko.components.register("tabs-manager", TabsManagerKOComponent());
|
||||||
|
|
||||||
// Collection Tabs
|
// Collection Tabs
|
||||||
ko.components.register("documents-tab", new TabComponents.DocumentsTab());
|
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 { Splitter, SplitterBounds, SplitterDirection } from "../Common/Splitter";
|
||||||
import { StringInputPane } from "./Panes/StringInputPane";
|
import { StringInputPane } from "./Panes/StringInputPane";
|
||||||
import { TableColumnOptionsPane } from "./Panes/Tables/TableColumnOptionsPane";
|
import { TableColumnOptionsPane } from "./Panes/Tables/TableColumnOptionsPane";
|
||||||
|
import { TabsManager } from "./Tabs/TabsManager";
|
||||||
import { UploadFilePane } from "./Panes/UploadFilePane";
|
import { UploadFilePane } from "./Panes/UploadFilePane";
|
||||||
import { UploadItemsPane } from "./Panes/UploadItemsPane";
|
import { UploadItemsPane } from "./Panes/UploadItemsPane";
|
||||||
import { UploadItemsPaneAdapter } from "./Panes/UploadItemsPaneAdapter";
|
import { UploadItemsPaneAdapter } from "./Panes/UploadItemsPaneAdapter";
|
||||||
@ -161,11 +162,10 @@ export default class Explorer implements ViewModels.Explorer {
|
|||||||
private resourceTreeForResourceToken: ResourceTreeAdapterForResourceToken;
|
private resourceTreeForResourceToken: ResourceTreeAdapterForResourceToken;
|
||||||
|
|
||||||
// Tabs
|
// Tabs
|
||||||
public openedTabs: ko.ObservableArray<ViewModels.Tab>;
|
|
||||||
public activeTab: ko.Observable<ViewModels.Tab>;
|
|
||||||
public isTabsContentExpanded: ko.Observable<boolean>;
|
public isTabsContentExpanded: ko.Observable<boolean>;
|
||||||
public galleryTab: any;
|
public galleryTab: any;
|
||||||
public notebookViewerTab: any;
|
public notebookViewerTab: any;
|
||||||
|
public tabsManager: TabsManager;
|
||||||
|
|
||||||
// Contextual panes
|
// Contextual panes
|
||||||
public addDatabasePane: ViewModels.AddDatabasePane;
|
public addDatabasePane: ViewModels.AddDatabasePane;
|
||||||
@ -229,7 +229,6 @@ export default class Explorer implements ViewModels.Explorer {
|
|||||||
public arcadiaWorkspaces: ko.ObservableArray<ArcadiaWorkspaceItem>;
|
public arcadiaWorkspaces: ko.ObservableArray<ArcadiaWorkspaceItem>;
|
||||||
public hasStorageAnalyticsAfecFeature: ko.Observable<boolean>;
|
public hasStorageAnalyticsAfecFeature: ko.Observable<boolean>;
|
||||||
public isSynapseLinkUpdating: ko.Observable<boolean>;
|
public isSynapseLinkUpdating: ko.Observable<boolean>;
|
||||||
public isNotebookTabActive: ko.Computed<boolean>;
|
|
||||||
public memoryUsageInfo: ko.Observable<DataModels.MemoryUsageInfo>;
|
public memoryUsageInfo: ko.Observable<DataModels.MemoryUsageInfo>;
|
||||||
public notebookManager?: any; // This is dynamically loaded
|
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 = ko.observable<string>();
|
||||||
this.arcadiaToken.subscribe((token: string) => {
|
this.arcadiaToken.subscribe((token: string) => {
|
||||||
if (token) {
|
if (token) {
|
||||||
this.openedTabs &&
|
const notebookTabs = this.tabsManager.getTabs(ViewModels.CollectionTabKind.NotebookV2);
|
||||||
this.openedTabs().forEach(tab => {
|
(notebookTabs || []).forEach((tab: NotebookV2Tab) => {
|
||||||
if (tab.tabKind === ViewModels.CollectionTabKind.Notebook) {
|
tab.reconfigureServiceEndpoints();
|
||||||
throw new Error("NotebookTab is deprecated. Use NotebookV2Tab");
|
});
|
||||||
} else if (tab.tabKind === ViewModels.CollectionTabKind.NotebookV2) {
|
|
||||||
(tab as NotebookV2Tab).reconfigureServiceEndpoints();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.isNotebooksEnabledForAccount = ko.observable(false);
|
this.isNotebooksEnabledForAccount = ko.observable(false);
|
||||||
@ -771,12 +766,8 @@ export default class Explorer implements ViewModels.Explorer {
|
|||||||
container: this
|
container: this
|
||||||
});
|
});
|
||||||
|
|
||||||
this.openedTabs = ko.observableArray<ViewModels.Tab>([]);
|
this.tabsManager = new TabsManager();
|
||||||
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._panes = [
|
this._panes = [
|
||||||
this.addDatabasePane,
|
this.addDatabasePane,
|
||||||
this.addCollectionPane,
|
this.addCollectionPane,
|
||||||
@ -1262,7 +1253,7 @@ export default class Explorer implements ViewModels.Explorer {
|
|||||||
class: "connectDialogButtons okBtn connectOkBtns",
|
class: "connectDialogButtons okBtn connectOkBtns",
|
||||||
click: () => {
|
click: () => {
|
||||||
$("#contextSwitchPrompt").dialog("close");
|
$("#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);
|
this.renewShareAccess(connectionString);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -2090,10 +2081,6 @@ export default class Explorer implements ViewModels.Explorer {
|
|||||||
return Q();
|
return Q();
|
||||||
}
|
}
|
||||||
|
|
||||||
public findActiveTab(): ViewModels.Tab {
|
|
||||||
return this.activeTab();
|
|
||||||
}
|
|
||||||
|
|
||||||
public findSelectedCollection(): ViewModels.Collection {
|
public findSelectedCollection(): ViewModels.Collection {
|
||||||
if (this.selectedNode().nodeKind === "Collection") {
|
if (this.selectedNode().nodeKind === "Collection") {
|
||||||
return this.findSelectedCollectionForSelectedNode();
|
return this.findSelectedCollectionForSelectedNode();
|
||||||
@ -2106,11 +2093,9 @@ export default class Explorer implements ViewModels.Explorer {
|
|||||||
public findSelectedStoredProcedure(): ViewModels.StoredProcedure {
|
public findSelectedStoredProcedure(): ViewModels.StoredProcedure {
|
||||||
const selectedCollection: ViewModels.Collection = this.findSelectedCollection();
|
const selectedCollection: ViewModels.Collection = this.findSelectedCollection();
|
||||||
return _.find(selectedCollection.storedProcedures(), (storedProcedure: ViewModels.StoredProcedure) => {
|
return _.find(selectedCollection.storedProcedures(), (storedProcedure: ViewModels.StoredProcedure) => {
|
||||||
const openedSprocTab = this.openedTabs().filter(
|
const openedSprocTab = this.tabsManager.getTabs(
|
||||||
(tab: ViewModels.Tab) =>
|
ViewModels.CollectionTabKind.StoredProcedures,
|
||||||
tab.node &&
|
(tab: ViewModels.Tab) => tab.node && tab.node.rid === storedProcedure.rid
|
||||||
tab.node.rid === storedProcedure.rid &&
|
|
||||||
tab.tabKind === ViewModels.CollectionTabKind.StoredProcedures
|
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
storedProcedure.rid === this.selectedNode().rid ||
|
storedProcedure.rid === this.selectedNode().rid ||
|
||||||
@ -2122,11 +2107,9 @@ export default class Explorer implements ViewModels.Explorer {
|
|||||||
public findSelectedUDF(): ViewModels.UserDefinedFunction {
|
public findSelectedUDF(): ViewModels.UserDefinedFunction {
|
||||||
const selectedCollection: ViewModels.Collection = this.findSelectedCollection();
|
const selectedCollection: ViewModels.Collection = this.findSelectedCollection();
|
||||||
return _.find(selectedCollection.userDefinedFunctions(), (userDefinedFunction: ViewModels.UserDefinedFunction) => {
|
return _.find(selectedCollection.userDefinedFunctions(), (userDefinedFunction: ViewModels.UserDefinedFunction) => {
|
||||||
const openedUdfTab = this.openedTabs().filter(
|
const openedUdfTab = this.tabsManager.getTabs(
|
||||||
(tab: ViewModels.Tab) =>
|
ViewModels.CollectionTabKind.UserDefinedFunctions,
|
||||||
tab.node &&
|
(tab: ViewModels.Tab) => tab.node && tab.node.rid === userDefinedFunction.rid
|
||||||
tab.node.rid === userDefinedFunction.rid &&
|
|
||||||
tab.tabKind === ViewModels.CollectionTabKind.UserDefinedFunctions
|
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
userDefinedFunction.rid === this.selectedNode().rid ||
|
userDefinedFunction.rid === this.selectedNode().rid ||
|
||||||
@ -2138,9 +2121,9 @@ export default class Explorer implements ViewModels.Explorer {
|
|||||||
public findSelectedTrigger(): ViewModels.Trigger {
|
public findSelectedTrigger(): ViewModels.Trigger {
|
||||||
const selectedCollection: ViewModels.Collection = this.findSelectedCollection();
|
const selectedCollection: ViewModels.Collection = this.findSelectedCollection();
|
||||||
return _.find(selectedCollection.triggers(), (trigger: ViewModels.Trigger) => {
|
return _.find(selectedCollection.triggers(), (trigger: ViewModels.Trigger) => {
|
||||||
const openedTriggerTab = this.openedTabs().filter(
|
const openedTriggerTab = this.tabsManager.getTabs(
|
||||||
(tab: ViewModels.Tab) =>
|
ViewModels.CollectionTabKind.Triggers,
|
||||||
tab.node && tab.node.rid === trigger.rid && tab.tabKind === ViewModels.CollectionTabKind.Triggers
|
(tab: ViewModels.Tab) => tab.node && tab.node.rid === trigger.rid
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
trigger.rid === this.selectedNode().rid ||
|
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 {
|
public closeAllPanes(): void {
|
||||||
this._panes.forEach((pane: ViewModels.ContextualPane) => pane.close());
|
this._panes.forEach((pane: ViewModels.ContextualPane) => pane.close());
|
||||||
}
|
}
|
||||||
@ -2235,7 +2208,9 @@ export default class Explorer implements ViewModels.Explorer {
|
|||||||
if (isNewDatabase) {
|
if (isNewDatabase) {
|
||||||
database.expandDatabase();
|
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 urlPrefixWithKeyParam: string = `${config.hostedExplorerURL}?key=`;
|
||||||
const currentActiveTab: ViewModels.Tab = this.findActiveTab();
|
const currentActiveTab: ViewModels.Tab = this.tabsManager.activeTab();
|
||||||
|
|
||||||
return `${urlPrefixWithKeyParam}${token}#/${(currentActiveTab && currentActiveTab.hashLocation()) || ""}`;
|
return `${urlPrefixWithKeyParam}${token}#/${(currentActiveTab && currentActiveTab.hashLocation()) || ""}`;
|
||||||
}
|
}
|
||||||
@ -2600,46 +2575,48 @@ export default class Explorer implements ViewModels.Explorer {
|
|||||||
if (!notebookContentItem || !notebookContentItem.path) {
|
if (!notebookContentItem || !notebookContentItem.path) {
|
||||||
throw new Error(`Invalid notebookContentItem: ${notebookContentItem}`);
|
throw new Error(`Invalid notebookContentItem: ${notebookContentItem}`);
|
||||||
}
|
}
|
||||||
const openedNotebookTabs = this.getNotebookTabsForFilepath(notebookContentItem.path);
|
|
||||||
|
|
||||||
if (openedNotebookTabs.length > 0) {
|
const notebookTabs: NotebookV2Tab[] = this.tabsManager.getTabs(
|
||||||
openedNotebookTabs[0].onTabClick();
|
ViewModels.CollectionTabKind.NotebookV2,
|
||||||
openedNotebookTabs[0].onActivate();
|
(tab: ViewModels.Tab) =>
|
||||||
return true;
|
(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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2652,10 +2629,11 @@ export default class Explorer implements ViewModels.Explorer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Don't delete if tab is open to avoid accidental deletion
|
// Don't delete if tab is open to avoid accidental deletion
|
||||||
const openedNotebookTabs = this.openedTabs().filter(
|
const openedNotebookTabs = this.tabsManager.getTabs(
|
||||||
(tab: ViewModels.Tab) =>
|
ViewModels.CollectionTabKind.NotebookV2,
|
||||||
tab.tabKind === ViewModels.CollectionTabKind.NotebookV2 &&
|
(tab: NotebookV2Tab) => {
|
||||||
(tab as NotebookV2Tab).notebookPath() === notebookFile.path
|
return tab.notebookPath && FileSystemUtil.isPathEqual(tab.notebookPath(), notebookFile.path);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
if (openedNotebookTabs.length > 0) {
|
if (openedNotebookTabs.length > 0) {
|
||||||
this.showOkModalDialog("Unable to rename file", "This file is being edited. Please close the tab and try again.");
|
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)
|
onSubmit: (input: string) => this.notebookManager?.notebookContentClient.renameNotebook(notebookFile, input)
|
||||||
})
|
})
|
||||||
.then(newNotebookFile => {
|
.then(newNotebookFile => {
|
||||||
this.openedTabs()
|
const notebookTabs: ViewModels.Tab[] = this.tabsManager.getTabs(
|
||||||
.filter(
|
ViewModels.CollectionTabKind.NotebookV2,
|
||||||
(tab: ViewModels.Tab) =>
|
(tab: NotebookV2Tab) => tab.notebookPath && FileSystemUtil.isPathEqual(tab.notebookPath(), originalPath)
|
||||||
tab.tabKind === ViewModels.CollectionTabKind.NotebookV2 &&
|
);
|
||||||
FileSystemUtil.isPathEqual((tab as NotebookV2Tab).notebookPath(), originalPath)
|
notebookTabs.forEach(tab => {
|
||||||
)
|
tab.tabTitle(newNotebookFile.name);
|
||||||
.forEach(tab => {
|
tab.tabPath(newNotebookFile.path);
|
||||||
tab.tabTitle(newNotebookFile.name);
|
(tab as NotebookV2Tab).notebookPath(newNotebookFile.path);
|
||||||
tab.tabPath(newNotebookFile.path);
|
});
|
||||||
(tab as NotebookV2Tab).notebookPath(newNotebookFile.path);
|
|
||||||
});
|
|
||||||
|
|
||||||
return newNotebookFile;
|
return newNotebookFile;
|
||||||
});
|
});
|
||||||
@ -2865,39 +2841,34 @@ export default class Explorer implements ViewModels.Explorer {
|
|||||||
await this.initSparkConnectionInfo(this.databaseAccount());
|
await this.initSparkConnectionInfo(this.databaseAccount());
|
||||||
}
|
}
|
||||||
|
|
||||||
const openedSparkMasterTabs = this.openedTabs().filter(
|
const sparkMasterTabs: SparkMasterTab[] = this.tabsManager.getTabs(
|
||||||
(tab: ViewModels.Tab) => tab.tabKind === ViewModels.CollectionTabKind.SparkMasterTab
|
ViewModels.CollectionTabKind.SparkMasterTab
|
||||||
);
|
) as SparkMasterTab[];
|
||||||
if (openedSparkMasterTabs.length > 0) {
|
let sparkMasterTab: SparkMasterTab = sparkMasterTabs && sparkMasterTabs[0];
|
||||||
openedSparkMasterTabs[0].onTabClick();
|
|
||||||
openedSparkMasterTabs[0].onActivate();
|
if (sparkMasterTab) {
|
||||||
return;
|
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> => {
|
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
|
// Don't delete if tab is open to avoid accidental deletion
|
||||||
const openedNotebookTabs = this.openedTabs().filter(
|
const openedNotebookTabs = this.tabsManager.getTabs(
|
||||||
(tab: ViewModels.Tab) =>
|
ViewModels.CollectionTabKind.NotebookV2,
|
||||||
tab.tabKind === ViewModels.CollectionTabKind.NotebookV2 && (tab as NotebookV2Tab).notebookPath() === item.path
|
(tab: NotebookV2Tab) => {
|
||||||
|
return tab.notebookPath && FileSystemUtil.isPathEqual(tab.notebookPath(), item.path);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
if (openedNotebookTabs.length > 0) {
|
if (openedNotebookTabs.length > 0) {
|
||||||
this.showOkModalDialog("Unable to delete file", "This file is being edited. Please close the tab and try again.");
|
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");
|
throw new Error("Terminal kind: ${kind} not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
const openedTabs = this.openedTabs().filter(
|
const terminalTabs: TerminalTab[] = this.tabsManager.getTabs(
|
||||||
(tab: ViewModels.Tab) => tab.tabKind === ViewModels.CollectionTabKind.Terminal
|
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 (terminalTab) {
|
||||||
if (openedTabs[i].hashLocation() == hashLocation) {
|
this.tabsManager.activateTab(terminalTab);
|
||||||
openedTabs[i].onTabClick();
|
} else {
|
||||||
openedTabs[i].onActivate();
|
const newTab = new TerminalTab({
|
||||||
return;
|
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) {
|
public async openGallery(notebookUrl?: string, galleryItem?: IGalleryItem, isFavorite?: boolean) {
|
||||||
let title: string;
|
let title: string = "Gallery";
|
||||||
let hashLocation: string;
|
let hashLocation: string = "gallery";
|
||||||
|
|
||||||
title = "Gallery";
|
const galleryTabs = this.tabsManager.getTabs(
|
||||||
hashLocation = "gallery";
|
ViewModels.CollectionTabKind.Gallery,
|
||||||
|
(tab: ViewModels.Tab) => tab.hashLocation() == hashLocation
|
||||||
const openedTabs = this.openedTabs().filter(
|
|
||||||
(tab: ViewModels.Tab) => tab.tabKind === ViewModels.CollectionTabKind.Gallery
|
|
||||||
);
|
);
|
||||||
|
let galleryTab = galleryTabs && galleryTabs[0];
|
||||||
|
|
||||||
for (let i = 0; i < openedTabs.length; ++i) {
|
if (galleryTab) {
|
||||||
if (openedTabs[i].hashLocation() == hashLocation) {
|
this.tabsManager.activateTab(galleryTab);
|
||||||
openedTabs[i].onTabClick();
|
(galleryTab as any).updateGalleryParams(notebookUrl, galleryItem, isFavorite);
|
||||||
openedTabs[i].onActivate();
|
} else {
|
||||||
(openedTabs[i] as any).updateGalleryParams(notebookUrl, galleryItem, isFavorite);
|
if (!this.galleryTab) {
|
||||||
return;
|
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) {
|
public async openNotebookViewer(notebookUrl: string) {
|
||||||
@ -3189,41 +3147,37 @@ export default class Explorer implements ViewModels.Explorer {
|
|||||||
return notebookViewerTab.notebookUrl === notebookUrl;
|
return notebookViewerTab.notebookUrl === notebookUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
const openedTabs = this.openedTabs().filter(
|
const notebookViewerTabs = this.tabsManager.getTabs(
|
||||||
(tab: ViewModels.Tab) => tab.tabKind === ViewModels.CollectionTabKind.NotebookViewer && isNotebookViewerOpen(tab)
|
ViewModels.CollectionTabKind.NotebookV2,
|
||||||
);
|
(tab: ViewModels.Tab) => {
|
||||||
|
return tab.hashLocation() == hashLocation && isNotebookViewerOpen(tab);
|
||||||
for (let i = 0; i < openedTabs.length; ++i) {
|
|
||||||
if (openedTabs[i].hashLocation() == hashLocation) {
|
|
||||||
openedTabs[i].onTabClick();
|
|
||||||
openedTabs[i].onActivate();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
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 {
|
public onNewCollectionClicked(): void {
|
||||||
@ -3235,24 +3189,8 @@ export default class Explorer implements ViewModels.Explorer {
|
|||||||
document.getElementById("linkAddCollection").focus();
|
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 {
|
private refreshCommandBarButtons(): void {
|
||||||
const activeTab = this.findActiveTab();
|
const activeTab = this.tabsManager.activeTab();
|
||||||
if (activeTab) {
|
if (activeTab) {
|
||||||
activeTab.onActivate(); // TODO only update tabs buttons?
|
activeTab.onActivate(); // TODO only update tabs buttons?
|
||||||
} else {
|
} else {
|
||||||
|
@ -16,10 +16,14 @@ export class CommandBarComponentAdapter implements ReactAdapter {
|
|||||||
public parameters: ko.Observable<number>;
|
public parameters: ko.Observable<number>;
|
||||||
public container: ViewModels.Explorer;
|
public container: ViewModels.Explorer;
|
||||||
private tabsButtons: ViewModels.NavbarButtonConfig[];
|
private tabsButtons: ViewModels.NavbarButtonConfig[];
|
||||||
|
private isNotebookTabActive: ko.Computed<boolean>;
|
||||||
|
|
||||||
constructor(container: ViewModels.Explorer) {
|
constructor(container: ViewModels.Explorer) {
|
||||||
this.container = container;
|
this.container = container;
|
||||||
this.tabsButtons = [];
|
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
|
// These are the parameters watched by the react binding that will trigger a renderComponent() if one of the ko mutates
|
||||||
const toWatch = [
|
const toWatch = [
|
||||||
@ -39,7 +43,7 @@ export class CommandBarComponentAdapter implements ReactAdapter {
|
|||||||
container.isHostedDataExplorerEnabled,
|
container.isHostedDataExplorerEnabled,
|
||||||
container.isSynapseLinkUpdating,
|
container.isSynapseLinkUpdating,
|
||||||
container.databaseAccount,
|
container.databaseAccount,
|
||||||
container.isNotebookTabActive
|
this.isNotebookTabActive
|
||||||
];
|
];
|
||||||
|
|
||||||
ko.computed(() => ko.toJSON(toWatch)).subscribe(() => this.triggerRender());
|
ko.computed(() => ko.toJSON(toWatch)).subscribe(() => this.triggerRender());
|
||||||
@ -74,7 +78,7 @@ export class CommandBarComponentAdapter implements ReactAdapter {
|
|||||||
const uiFabricControlButtons = CommandBarUtil.convertButton(controlButtons, backgroundColor);
|
const uiFabricControlButtons = CommandBarUtil.convertButton(controlButtons, backgroundColor);
|
||||||
uiFabricControlButtons.forEach((btn: ICommandBarItemProps) => (btn.iconOnly = true));
|
uiFabricControlButtons.forEach((btn: ICommandBarItemProps) => (btn.iconOnly = true));
|
||||||
|
|
||||||
if (this.container && this.container.isNotebookTabActive()) {
|
if (this.isNotebookTabActive()) {
|
||||||
uiFabricControlButtons.unshift(
|
uiFabricControlButtons.unshift(
|
||||||
CommandBarUtil.createMemoryTracker("memoryTracker", this.container.memoryUsageInfo)
|
CommandBarUtil.createMemoryTracker("memoryTracker", this.container.memoryUsageInfo)
|
||||||
);
|
);
|
||||||
|
@ -43,6 +43,7 @@ import { CdbAppState } from "./types";
|
|||||||
import { decryptJWTToken } from "../../../Utils/AuthorizationUtils";
|
import { decryptJWTToken } from "../../../Utils/AuthorizationUtils";
|
||||||
import * as TextFile from "./contents/file/text-file";
|
import * as TextFile from "./contents/file/text-file";
|
||||||
import { NotebookUtil } from "../NotebookUtil";
|
import { NotebookUtil } from "../NotebookUtil";
|
||||||
|
import { FileSystemUtil } from "../FileSystemUtil";
|
||||||
|
|
||||||
interface NotebookServiceConfig extends JupyterServerConfig {
|
interface NotebookServiceConfig extends JupyterServerConfig {
|
||||||
userPuid?: string;
|
userPuid?: string;
|
||||||
@ -806,7 +807,9 @@ const closeUnsupportedMimetypesEpic = (
|
|||||||
if (explorer && !TextFile.handles(mimetype)) {
|
if (explorer && !TextFile.handles(mimetype)) {
|
||||||
const filepath = action.payload.filepath;
|
const filepath = action.payload.filepath;
|
||||||
// Close tab and show error message
|
// 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.`;
|
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);
|
explorer.showOkModalDialog("File cannot be rendered", msg);
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, msg);
|
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, msg);
|
||||||
@ -832,7 +835,9 @@ const closeContentFailedToFetchEpic = (
|
|||||||
if (explorer) {
|
if (explorer) {
|
||||||
const filepath = action.payload.filepath;
|
const filepath = action.payload.filepath;
|
||||||
// Close tab and show error message
|
// 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}.`;
|
const msg = `Failed to load file: ${filepath}.`;
|
||||||
explorer.showOkModalDialog("Failure to load", msg);
|
explorer.showOkModalDialog("Failure to load", msg);
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, msg);
|
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, msg);
|
||||||
|
@ -22,6 +22,7 @@ import { Versions } from "../../src/Contracts/ExplorerContracts";
|
|||||||
import { CollectionCreationDefaults } from "../Shared/Constants";
|
import { CollectionCreationDefaults } from "../Shared/Constants";
|
||||||
import { IGalleryItem } from "../Juno/JunoClient";
|
import { IGalleryItem } from "../Juno/JunoClient";
|
||||||
import { ReactAdapter } from "../Bindings/ReactBindingHandler";
|
import { ReactAdapter } from "../Bindings/ReactBindingHandler";
|
||||||
|
import { TabsManager } from "./Tabs/TabsManager";
|
||||||
|
|
||||||
export class ExplorerStub implements ViewModels.Explorer {
|
export class ExplorerStub implements ViewModels.Explorer {
|
||||||
public flight: ko.Observable<string>;
|
public flight: ko.Observable<string>;
|
||||||
@ -69,7 +70,6 @@ export class ExplorerStub implements ViewModels.Explorer {
|
|||||||
public isLeftPaneExpanded: ko.Observable<boolean>;
|
public isLeftPaneExpanded: ko.Observable<boolean>;
|
||||||
public selectedNode: ko.Observable<ViewModels.TreeNode>;
|
public selectedNode: ko.Observable<ViewModels.TreeNode>;
|
||||||
public isRefreshingExplorer: ko.Observable<boolean>;
|
public isRefreshingExplorer: ko.Observable<boolean>;
|
||||||
public openedTabs: ko.ObservableArray<ViewModels.Tab>;
|
|
||||||
public isTabsContentExpanded: ko.Observable<boolean>;
|
public isTabsContentExpanded: ko.Observable<boolean>;
|
||||||
public addCollectionPane: ViewModels.AddCollectionPane;
|
public addCollectionPane: ViewModels.AddCollectionPane;
|
||||||
public addDatabasePane: ViewModels.AddDatabasePane;
|
public addDatabasePane: ViewModels.AddDatabasePane;
|
||||||
@ -101,7 +101,6 @@ export class ExplorerStub implements ViewModels.Explorer {
|
|||||||
public canExceedMaximumValue: ko.Computed<boolean>;
|
public canExceedMaximumValue: ko.Computed<boolean>;
|
||||||
public isHostedDataExplorerEnabled: ko.Computed<boolean>;
|
public isHostedDataExplorerEnabled: ko.Computed<boolean>;
|
||||||
public parentFrameDataExplorerVersion: ko.Observable<string> = ko.observable<string>(Versions.DataExplorer);
|
public parentFrameDataExplorerVersion: ko.Observable<string> = ko.observable<string>(Versions.DataExplorer);
|
||||||
public activeTab: ko.Observable<ViewModels.Tab>;
|
|
||||||
public mostRecentActivity: MostRecentActivity;
|
public mostRecentActivity: MostRecentActivity;
|
||||||
public isNotebookEnabled: ko.Observable<boolean>;
|
public isNotebookEnabled: ko.Observable<boolean>;
|
||||||
public isSparkEnabled: ko.Observable<boolean>;
|
public isSparkEnabled: ko.Observable<boolean>;
|
||||||
@ -119,7 +118,6 @@ export class ExplorerStub implements ViewModels.Explorer {
|
|||||||
public arcadiaWorkspaces: ko.ObservableArray<ArcadiaWorkspaceItem>;
|
public arcadiaWorkspaces: ko.ObservableArray<ArcadiaWorkspaceItem>;
|
||||||
public hasStorageAnalyticsAfecFeature: ko.Observable<boolean>;
|
public hasStorageAnalyticsAfecFeature: ko.Observable<boolean>;
|
||||||
public isSynapseLinkUpdating: ko.Observable<boolean>;
|
public isSynapseLinkUpdating: ko.Observable<boolean>;
|
||||||
public isNotebookTabActive: ko.Computed<boolean>;
|
|
||||||
public memoryUsageInfo: ko.Observable<DataModels.MemoryUsageInfo>;
|
public memoryUsageInfo: ko.Observable<DataModels.MemoryUsageInfo>;
|
||||||
public notebookManager?: any;
|
public notebookManager?: any;
|
||||||
public openGallery: (notebookUrl?: string, galleryItem?: IGalleryItem, isFavorite?: boolean) => void;
|
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 resourceTokenPartitionKey: ko.Observable<string>;
|
||||||
public isAuthWithResourceToken: ko.Observable<boolean>;
|
public isAuthWithResourceToken: ko.Observable<boolean>;
|
||||||
public isResourceTokenCollectionNodeSelected: ko.Computed<boolean>;
|
public isResourceTokenCollectionNodeSelected: ko.Computed<boolean>;
|
||||||
|
public tabsManager: TabsManager;
|
||||||
|
|
||||||
private _featureEnabledReturnValue: boolean;
|
private _featureEnabledReturnValue: boolean;
|
||||||
|
|
||||||
@ -248,10 +247,6 @@ export class ExplorerStub implements ViewModels.Explorer {
|
|||||||
throw new Error("Not implemented");
|
throw new Error("Not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
public findActiveTab(): ViewModels.Tab {
|
|
||||||
throw new Error("Not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
public findSelectedStoredProcedure(): ViewModels.StoredProcedure {
|
public findSelectedStoredProcedure(): ViewModels.StoredProcedure {
|
||||||
throw new Error("Not implemented");
|
throw new Error("Not implemented");
|
||||||
}
|
}
|
||||||
@ -300,10 +295,6 @@ export class ExplorerStub implements ViewModels.Explorer {
|
|||||||
throw new Error("Not implemented");
|
throw new Error("Not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
public closeAllTabsForResource(resourceId: string): void {
|
|
||||||
throw new Error("Not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
public getPlatformType(): PlatformType {
|
public getPlatformType(): PlatformType {
|
||||||
throw new Error("Not implemented");
|
throw new Error("Not implemented");
|
||||||
}
|
}
|
||||||
@ -426,10 +417,6 @@ export class ExplorerStub implements ViewModels.Explorer {
|
|||||||
throw new Error("Not implemented");
|
throw new Error("Not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
public closeNotebookTab(filepath: string): void {
|
|
||||||
throw new Error("Not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
public refreshContentItem(item: NotebookContentItem): Promise<void> {
|
public refreshContentItem(item: NotebookContentItem): Promise<void> {
|
||||||
throw new Error("Not implemented");
|
throw new Error("Not implemented");
|
||||||
}
|
}
|
||||||
@ -511,10 +498,6 @@ export class DatabaseStub implements ViewModels.Database {
|
|||||||
throw new Error("Not implemented");
|
throw new Error("Not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
public refreshTabSelectedState(): void {
|
|
||||||
throw new Error("Not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
public readSettings() {
|
public readSettings() {
|
||||||
throw new Error("Not implemented");
|
throw new Error("Not implemented");
|
||||||
}
|
}
|
||||||
@ -779,10 +762,6 @@ export class CollectionStub implements ViewModels.Collection {
|
|||||||
throw new Error("Not implemented");
|
throw new Error("Not implemented");
|
||||||
};
|
};
|
||||||
|
|
||||||
public refreshActiveTab = (): void => {
|
|
||||||
throw new Error("Not implemented");
|
|
||||||
};
|
|
||||||
|
|
||||||
public getLabel(): string {
|
public getLabel(): string {
|
||||||
throw new Error("Not implemented");
|
throw new Error("Not implemented");
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ export class BrowseQueriesPane extends ContextualPaneBase implements ViewModels.
|
|||||||
} else {
|
} else {
|
||||||
selectedCollection.onNewQueryClick(selectedCollection, null);
|
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.tabTitle(savedQuery.queryName);
|
||||||
queryTab.tabPath(`${selectedCollection.databaseId}>${selectedCollection.id()}>${savedQuery.queryName}`);
|
queryTab.tabPath(`${selectedCollection.databaseId}>${selectedCollection.id()}>${savedQuery.queryName}`);
|
||||||
queryTab.initialEditorContent(savedQuery.query);
|
queryTab.initialEditorContent(savedQuery.query);
|
||||||
|
@ -66,7 +66,9 @@ export default class DeleteCollectionConfirmationPane extends ContextualPaneBase
|
|||||||
this.isExecuting(false);
|
this.isExecuting(false);
|
||||||
this.close();
|
this.close();
|
||||||
this.container.selectedNode(selectedCollection.database);
|
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.container.refreshAllDatabases();
|
||||||
this.resetData();
|
this.resetData();
|
||||||
TelemetryProcessor.traceSuccess(
|
TelemetryProcessor.traceSuccess(
|
||||||
|
@ -11,6 +11,7 @@ import Explorer from "../Explorer";
|
|||||||
import { CollectionStub, DatabaseStub, ExplorerStub } from "../OpenActionsStubs";
|
import { CollectionStub, DatabaseStub, ExplorerStub } from "../OpenActionsStubs";
|
||||||
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import { TreeNode } from "../../Contracts/ViewModels";
|
import { TreeNode } from "../../Contracts/ViewModels";
|
||||||
|
import { TabsManager } from "../Tabs/TabsManager";
|
||||||
|
|
||||||
describe("Delete Database Confirmation Pane", () => {
|
describe("Delete Database Confirmation Pane", () => {
|
||||||
describe("Explorer.isLastDatabase() and Explorer.isLastNonEmptyDatabase()", () => {
|
describe("Explorer.isLastDatabase() and Explorer.isLastNonEmptyDatabase()", () => {
|
||||||
@ -123,6 +124,7 @@ describe("Delete Database Confirmation Pane", () => {
|
|||||||
);
|
);
|
||||||
sinon.stub(fakeExplorer, "documentClientUtility").value(fakeDocumentClientUtility);
|
sinon.stub(fakeExplorer, "documentClientUtility").value(fakeDocumentClientUtility);
|
||||||
sinon.stub(fakeExplorer, "selectedNode").value(ko.observable<TreeNode>());
|
sinon.stub(fakeExplorer, "selectedNode").value(ko.observable<TreeNode>());
|
||||||
|
sinon.stub(fakeExplorer, "tabsManager").value(new TabsManager());
|
||||||
fakeExplorer.isLastNonEmptyDatabase.returns(true);
|
fakeExplorer.isLastNonEmptyDatabase.returns(true);
|
||||||
|
|
||||||
let pane = new DeleteDatabaseConfirmationPane({
|
let pane = new DeleteDatabaseConfirmationPane({
|
||||||
|
@ -67,11 +67,17 @@ export default class DeleteDatabaseConfirmationPane extends ContextualPaneBase
|
|||||||
this.isExecuting(false);
|
this.isExecuting(false);
|
||||||
this.close();
|
this.close();
|
||||||
this.container.refreshAllDatabases();
|
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);
|
this.container.selectedNode(null);
|
||||||
selectedDatabase
|
selectedDatabase
|
||||||
.collections()
|
.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();
|
this.resetData();
|
||||||
TelemetryProcessor.traceSuccess(
|
TelemetryProcessor.traceSuccess(
|
||||||
Action.DeleteDatabase,
|
Action.DeleteDatabase,
|
||||||
|
@ -111,7 +111,7 @@ export class LoadQueryPane extends ContextualPaneBase implements ViewModels.Load
|
|||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = (evt: any): void => {
|
reader.onload = (evt: any): void => {
|
||||||
const fileData: string = evt.target.result;
|
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.initialEditorContent(fileData);
|
||||||
queryTab.sqlQueryEditorContent(fileData);
|
queryTab.sqlQueryEditorContent(fileData);
|
||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
|
@ -53,7 +53,7 @@ export class RenewAdHocAccessPane extends ContextualPaneBase implements ViewMode
|
|||||||
const apiKind: DataModels.ApiKind =
|
const apiKind: DataModels.ApiKind =
|
||||||
this.container && DefaultExperienceUtility.getApiKindFromDefaultExperience(this.container.defaultExperience());
|
this.container && DefaultExperienceUtility.getApiKindFromDefaultExperience(this.container.defaultExperience());
|
||||||
const hasOpenedTabs: boolean =
|
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) {
|
if (!inputMetadata || inputMetadata.apiKind == null || !inputMetadata.accountName) {
|
||||||
this.formErrors("Invalid connection string input");
|
this.formErrors("Invalid connection string input");
|
||||||
|
@ -34,7 +34,8 @@ export class SaveQueryPane extends ContextualPaneBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const queryName: string = this.queryName();
|
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();
|
const query: string = queryTab && queryTab.sqlQueryEditorContent();
|
||||||
if (!queryName || queryName.length === 0) {
|
if (!queryName || queryName.length === 0) {
|
||||||
this.formErrors("No query name specified");
|
this.formErrors("No query name specified");
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import { DataSamplesUtil } from "../DataSamples/DataSamplesUtil";
|
import { DataSamplesUtil } from "../DataSamples/DataSamplesUtil";
|
||||||
import { SplashScreenComponentAdapter } from "./SplashScreenComponentApdapter";
|
import { SplashScreenComponentAdapter } from "./SplashScreenComponentApdapter";
|
||||||
|
import { TabsManager } from "../Tabs/TabsManager";
|
||||||
import Explorer from "../Explorer";
|
import Explorer from "../Explorer";
|
||||||
jest.mock("../Explorer");
|
jest.mock("../Explorer");
|
||||||
|
|
||||||
const createExplorer = () => {
|
const createExplorer = () => {
|
||||||
const mock = new Explorer({} as any);
|
const mock = new Explorer({} as any);
|
||||||
mock.openedTabs = ko.observableArray([]);
|
|
||||||
mock.selectedNode = ko.observable();
|
mock.selectedNode = ko.observable();
|
||||||
mock.isNotebookEnabled = ko.observable(false);
|
mock.isNotebookEnabled = ko.observable(false);
|
||||||
mock.addCollectionText = ko.observable("add collection");
|
mock.addCollectionText = ko.observable("add collection");
|
||||||
|
mock.tabsManager = new TabsManager();
|
||||||
return mock as jest.Mocked<Explorer>;
|
return mock as jest.Mocked<Explorer>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ export class SplashScreenComponentAdapter implements ReactAdapter {
|
|||||||
|
|
||||||
constructor(private container: ViewModels.Explorer) {
|
constructor(private container: ViewModels.Explorer) {
|
||||||
this.parameters = ko.observable<number>(Date.now());
|
this.parameters = ko.observable<number>(Date.now());
|
||||||
this.container.openedTabs.subscribe(tabs => {
|
this.container.tabsManager.openedTabs.subscribe((tabs: ViewModels.Tab[]) => {
|
||||||
if (tabs.length === 0) {
|
if (tabs.length === 0) {
|
||||||
this.forceRender();
|
this.forceRender();
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ import DeleteIcon from "../../../images/delete.svg";
|
|||||||
import { QueryIterator, ItemDefinition, Resource, ConflictDefinition } from "@azure/cosmos";
|
import { QueryIterator, ItemDefinition, Resource, ConflictDefinition } from "@azure/cosmos";
|
||||||
import { MinimalQueryIterator } from "../../Common/IteratorUtilities";
|
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 selectedConflictId: ko.Observable<ViewModels.ConflictId>;
|
||||||
public selectedConflictContent: ViewModels.Editable<string>;
|
public selectedConflictContent: ViewModels.Editable<string>;
|
||||||
public selectedConflictCurrent: ViewModels.Editable<string>;
|
public selectedConflictCurrent: ViewModels.Editable<string>;
|
||||||
|
@ -20,8 +20,7 @@ describe("Documents tab", () => {
|
|||||||
hashLocation: "",
|
hashLocation: "",
|
||||||
isActive: ko.observable<boolean>(false),
|
isActive: ko.observable<boolean>(false),
|
||||||
|
|
||||||
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
|
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
|
||||||
openedTabs: []
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(documentsTab.buildQuery("")).toContain("select");
|
expect(documentsTab.buildQuery("")).toContain("select");
|
||||||
@ -104,8 +103,7 @@ describe("Documents tab", () => {
|
|||||||
hashLocation: "",
|
hashLocation: "",
|
||||||
isActive: ko.observable<boolean>(false),
|
isActive: ko.observable<boolean>(false),
|
||||||
|
|
||||||
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
|
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
|
||||||
openedTabs: []
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(documentsTab.showPartitionKey).toBe(false);
|
expect(documentsTab.showPartitionKey).toBe(false);
|
||||||
@ -124,8 +122,7 @@ describe("Documents tab", () => {
|
|||||||
hashLocation: "",
|
hashLocation: "",
|
||||||
isActive: ko.observable<boolean>(false),
|
isActive: ko.observable<boolean>(false),
|
||||||
|
|
||||||
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
|
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
|
||||||
openedTabs: []
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(documentsTab.showPartitionKey).toBe(false);
|
expect(documentsTab.showPartitionKey).toBe(false);
|
||||||
@ -144,8 +141,7 @@ describe("Documents tab", () => {
|
|||||||
hashLocation: "",
|
hashLocation: "",
|
||||||
isActive: ko.observable<boolean>(false),
|
isActive: ko.observable<boolean>(false),
|
||||||
|
|
||||||
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
|
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
|
||||||
openedTabs: []
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(documentsTab.showPartitionKey).toBe(true);
|
expect(documentsTab.showPartitionKey).toBe(true);
|
||||||
@ -164,8 +160,7 @@ describe("Documents tab", () => {
|
|||||||
hashLocation: "",
|
hashLocation: "",
|
||||||
isActive: ko.observable<boolean>(false),
|
isActive: ko.observable<boolean>(false),
|
||||||
|
|
||||||
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
|
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
|
||||||
openedTabs: []
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(documentsTab.showPartitionKey).toBe(false);
|
expect(documentsTab.showPartitionKey).toBe(false);
|
||||||
@ -184,8 +179,7 @@ describe("Documents tab", () => {
|
|||||||
hashLocation: "",
|
hashLocation: "",
|
||||||
isActive: ko.observable<boolean>(false),
|
isActive: ko.observable<boolean>(false),
|
||||||
|
|
||||||
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
|
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
|
||||||
openedTabs: []
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(documentsTab.showPartitionKey).toBe(true);
|
expect(documentsTab.showPartitionKey).toBe(true);
|
||||||
|
@ -28,8 +28,7 @@ describe("Query Tab", () => {
|
|||||||
selfLink: "",
|
selfLink: "",
|
||||||
isActive: ko.observable<boolean>(false),
|
isActive: ko.observable<boolean>(false),
|
||||||
hashLocation: "",
|
hashLocation: "",
|
||||||
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
|
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
|
||||||
openedTabs: []
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,8 +76,7 @@ describe("Settings tab", () => {
|
|||||||
quotaInfo,
|
quotaInfo,
|
||||||
null
|
null
|
||||||
),
|
),
|
||||||
onUpdateTabsButtons: undefined,
|
onUpdateTabsButtons: undefined
|
||||||
openedTabs: []
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -196,8 +195,7 @@ describe("Settings tab", () => {
|
|||||||
hashLocation: "",
|
hashLocation: "",
|
||||||
isActive: ko.observable(false),
|
isActive: ko.observable(false),
|
||||||
collection: new Collection(explorer, "mydb", baseCollection, quotaInfo, null),
|
collection: new Collection(explorer, "mydb", baseCollection, quotaInfo, null),
|
||||||
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
|
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
|
||||||
openedTabs: []
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(settingsTab.shouldUpdateCollection()).toBe(false);
|
expect(settingsTab.shouldUpdateCollection()).toBe(false);
|
||||||
@ -221,8 +219,7 @@ describe("Settings tab", () => {
|
|||||||
hashLocation: "",
|
hashLocation: "",
|
||||||
isActive: ko.observable(false),
|
isActive: ko.observable(false),
|
||||||
collection: new Collection(explorer, "mydb", baseCollection, quotaInfo, null),
|
collection: new Collection(explorer, "mydb", baseCollection, quotaInfo, null),
|
||||||
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
|
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
|
||||||
openedTabs: []
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(settingsTab.shouldUpdateCollection()).toBe(false);
|
expect(settingsTab.shouldUpdateCollection()).toBe(false);
|
||||||
@ -241,8 +238,7 @@ describe("Settings tab", () => {
|
|||||||
hashLocation: "",
|
hashLocation: "",
|
||||||
isActive: ko.observable(false),
|
isActive: ko.observable(false),
|
||||||
collection: new Collection(explorer, "mydb", baseCollection, quotaInfo, null),
|
collection: new Collection(explorer, "mydb", baseCollection, quotaInfo, null),
|
||||||
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
|
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
|
||||||
openedTabs: []
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(settingsTab.shouldUpdateCollection()).toBe(false);
|
expect(settingsTab.shouldUpdateCollection()).toBe(false);
|
||||||
@ -281,8 +277,7 @@ describe("Settings tab", () => {
|
|||||||
hashLocation: "",
|
hashLocation: "",
|
||||||
isActive: ko.observable(false),
|
isActive: ko.observable(false),
|
||||||
collection: new Collection(explorer, "mydb", baseCollection, quotaInfo, null),
|
collection: new Collection(explorer, "mydb", baseCollection, quotaInfo, null),
|
||||||
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
|
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
|
||||||
openedTabs: []
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(settingsTab.getUpdatedConflictResolutionPolicy()).toBe(null);
|
expect(settingsTab.getUpdatedConflictResolutionPolicy()).toBe(null);
|
||||||
@ -299,8 +294,7 @@ describe("Settings tab", () => {
|
|||||||
hashLocation: "",
|
hashLocation: "",
|
||||||
isActive: ko.observable(false),
|
isActive: ko.observable(false),
|
||||||
collection: new Collection(explorer, "mydb", baseCollection, quotaInfo, null),
|
collection: new Collection(explorer, "mydb", baseCollection, quotaInfo, null),
|
||||||
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
|
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
|
||||||
openedTabs: []
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(settingsTab.getUpdatedConflictResolutionPolicy()).toBe(null);
|
expect(settingsTab.getUpdatedConflictResolutionPolicy()).toBe(null);
|
||||||
@ -326,8 +320,7 @@ describe("Settings tab", () => {
|
|||||||
hashLocation: "",
|
hashLocation: "",
|
||||||
isActive: ko.observable(false),
|
isActive: ko.observable(false),
|
||||||
collection: new Collection(explorer, "mydb", baseCollection, quotaInfo, null),
|
collection: new Collection(explorer, "mydb", baseCollection, quotaInfo, null),
|
||||||
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
|
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
|
||||||
openedTabs: []
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(settingsTab.getUpdatedConflictResolutionPolicy()).toBe(null);
|
expect(settingsTab.getUpdatedConflictResolutionPolicy()).toBe(null);
|
||||||
@ -408,8 +401,7 @@ describe("Settings tab", () => {
|
|||||||
hashLocation: "",
|
hashLocation: "",
|
||||||
isActive: ko.observable(false),
|
isActive: ko.observable(false),
|
||||||
collection: getCollection(defaultApi, partitionKeyOption),
|
collection: getCollection(defaultApi, partitionKeyOption),
|
||||||
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
|
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
|
||||||
openedTabs: []
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -555,8 +547,7 @@ describe("Settings tab", () => {
|
|||||||
hashLocation: "",
|
hashLocation: "",
|
||||||
isActive: ko.observable(false),
|
isActive: ko.observable(false),
|
||||||
collection: getCollection(autoPilotTier),
|
collection: getCollection(autoPilotTier),
|
||||||
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {},
|
onUpdateTabsButtons: (buttons: ViewModels.NavbarButtonConfig[]): void => {}
|
||||||
openedTabs: []
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
describe("Visible", () => {
|
describe("Visible", () => {
|
||||||
|
@ -252,7 +252,7 @@ export default class SettingsTab extends TabsBase implements ViewModels.Settings
|
|||||||
this.throughputModeRadioName = `throughputModeRadio${this.tabId}`;
|
this.throughputModeRadioName = `throughputModeRadio${this.tabId}`;
|
||||||
|
|
||||||
this.changeFeedPolicyToggled = editable.observable<ChangeFeedPolicyToggledState>(
|
this.changeFeedPolicyToggled = editable.observable<ChangeFeedPolicyToggledState>(
|
||||||
this.collection.rawDataModel.changeFeedPolicy != null
|
this.collection.rawDataModel?.changeFeedPolicy != null
|
||||||
? ChangeFeedPolicyToggledState.On
|
? ChangeFeedPolicyToggledState.On
|
||||||
: ChangeFeedPolicyToggledState.Off
|
: ChangeFeedPolicyToggledState.Off
|
||||||
);
|
);
|
||||||
|
@ -16,6 +16,7 @@ import TriggerTabTemplate from "./TriggerTab.html";
|
|||||||
import UserDefinedFunctionTabTemplate from "./UserDefinedFunctionTab.html";
|
import UserDefinedFunctionTabTemplate from "./UserDefinedFunctionTab.html";
|
||||||
import GalleryTabTemplate from "./GalleryTab.html";
|
import GalleryTabTemplate from "./GalleryTab.html";
|
||||||
import NotebookViewerTabTemplate from "./NotebookViewerTab.html";
|
import NotebookViewerTabTemplate from "./NotebookViewerTab.html";
|
||||||
|
import TabsManagerTemplate from "./TabsManager.html";
|
||||||
|
|
||||||
export class TabComponent {
|
export class TabComponent {
|
||||||
constructor(data: any) {
|
constructor(data: any) {
|
||||||
@ -23,6 +24,15 @@ export class TabComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class TabsManager {
|
||||||
|
constructor() {
|
||||||
|
return {
|
||||||
|
viewModel: TabComponent,
|
||||||
|
template: TabsManagerTemplate
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class DocumentsTab {
|
export class DocumentsTab {
|
||||||
constructor() {
|
constructor() {
|
||||||
return {
|
return {
|
||||||
|
@ -24,8 +24,6 @@ export default class TabsBase extends WaitsForTemplateViewModel implements ViewM
|
|||||||
public tabKind: ViewModels.CollectionTabKind;
|
public tabKind: ViewModels.CollectionTabKind;
|
||||||
public tabTitle: ko.Observable<string>;
|
public tabTitle: ko.Observable<string>;
|
||||||
public tabPath: 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 closeButtonTabIndex: ko.Computed<number>;
|
||||||
public errorDetailsTabIndex: ko.Computed<number>;
|
public errorDetailsTabIndex: ko.Computed<number>;
|
||||||
public hashLocation: ko.Observable<string>;
|
public hashLocation: ko.Observable<string>;
|
||||||
@ -55,8 +53,6 @@ export default class TabsBase extends WaitsForTemplateViewModel implements ViewM
|
|||||||
(options.tabPath && ko.observable<string>(options.tabPath)) ||
|
(options.tabPath && ko.observable<string>(options.tabPath)) ||
|
||||||
(this.collection &&
|
(this.collection &&
|
||||||
ko.observable<string>(`${this.collection.databaseId}>${this.collection.id()}>${this.tabTitle()}`));
|
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.closeButtonTabIndex = ko.computed<number>(() => (this.isActive() ? 0 : null));
|
||||||
this.errorDetailsTabIndex = ko.computed<number>(() => (this.isActive() ? 0 : null));
|
this.errorDetailsTabIndex = ko.computed<number>(() => (this.isActive() ? 0 : null));
|
||||||
this.isExecutionError = ko.observable<boolean>(false);
|
this.isExecutionError = ko.observable<boolean>(false);
|
||||||
@ -80,34 +76,11 @@ export default class TabsBase extends WaitsForTemplateViewModel implements ViewM
|
|||||||
return true;
|
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> {
|
public onCloseTabButtonClick(): void {
|
||||||
const previousTab = this.previousTab();
|
const explorer: ViewModels.Explorer = this.getContainer();
|
||||||
const nextTab = this.nextTab();
|
explorer.tabsManager.closeTab(this.tabId, explorer);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
TelemetryProcessor.trace(Action.Tab, ActionModifiers.Close, {
|
TelemetryProcessor.trace(Action.Tab, ActionModifiers.Close, {
|
||||||
databaseAccountName: this.getContainer().databaseAccount().name,
|
databaseAccountName: this.getContainer().databaseAccount().name,
|
||||||
@ -115,16 +88,10 @@ export default class TabsBase extends WaitsForTemplateViewModel implements ViewM
|
|||||||
dataExplorerArea: Constants.Areas.Tab,
|
dataExplorerArea: Constants.Areas.Tab,
|
||||||
tabTitle: this.tabTitle()
|
tabTitle: this.tabTitle()
|
||||||
});
|
});
|
||||||
return Q();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public onTabClick(): Q.Promise<any> {
|
public onTabClick(): Q.Promise<any> {
|
||||||
for (let i = 0; i < this.getContainer().openedTabs().length; i++) {
|
this.getContainer().tabsManager.activateTab(this);
|
||||||
const tab = this.getContainer().openedTabs()[i];
|
|
||||||
tab.isActive(false);
|
|
||||||
}
|
|
||||||
this.isActive(true);
|
|
||||||
this.getContainer().activeTab(this);
|
|
||||||
return Q();
|
return Q();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
148
src/Explorer/Tabs/TabsManager.html
Normal file
148
src/Explorer/Tabs/TabsManager.html
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
<div id="content" class="flexContainer hideOverflows" data-bind="visible: openedTabs && openedTabs().length > 0">
|
||||||
|
<!-- Tabs - Start -->
|
||||||
|
<div class="nav-tabs-margin">
|
||||||
|
<ul class="nav nav-tabs level navTabHeight" id="navTabs" role="tablist">
|
||||||
|
<!-- ko foreach: openedTabs -->
|
||||||
|
<li
|
||||||
|
class="tabList"
|
||||||
|
data-bind="
|
||||||
|
attr: {
|
||||||
|
title: $data.tabPath,
|
||||||
|
'aria-selected': $data.isActive,
|
||||||
|
'aria-expanded': $data.isActive,
|
||||||
|
'aria-controls': $data.tabId
|
||||||
|
},
|
||||||
|
css:{
|
||||||
|
active: $data.isActive
|
||||||
|
},
|
||||||
|
hasFocus: $data.hasFocus,
|
||||||
|
event: { keypress: onKeyPressActivate },
|
||||||
|
click: $data.onTabClick,"
|
||||||
|
tabindex="0"
|
||||||
|
role="tab"
|
||||||
|
>
|
||||||
|
<span class="tabNavContentContainer">
|
||||||
|
<a data-toggle="tab" data-bind="attr: { href: '#' + $data.tabId }" tabindex="-1">
|
||||||
|
<div class="tab_Content">
|
||||||
|
<span class="statusIconContainer">
|
||||||
|
<div
|
||||||
|
class="errorIconContainer"
|
||||||
|
id="errorStatusIcon"
|
||||||
|
title="Click to view more details"
|
||||||
|
role="button"
|
||||||
|
data-bind="
|
||||||
|
click: onErrorDetailsClick,
|
||||||
|
event: { keypress: onErrorDetailsKeyPress },
|
||||||
|
attr: { tabindex: errorDetailsTabIndex },
|
||||||
|
css: { actionsEnabled: isActive },
|
||||||
|
visible: isExecutionError"
|
||||||
|
>
|
||||||
|
<span class="errorIcon"></span>
|
||||||
|
</div>
|
||||||
|
<img
|
||||||
|
class="loadingIcon"
|
||||||
|
title="Loading"
|
||||||
|
src="/circular_loader_black_16x16.gif"
|
||||||
|
data-bind="visible: $data.isExecuting"
|
||||||
|
alt="Loading"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span class="tabNavText" data-bind="text: $data.tabTitle"></span>
|
||||||
|
<span class="tabIconSection">
|
||||||
|
<span
|
||||||
|
aria-label="Close Tab"
|
||||||
|
role="button"
|
||||||
|
class="cancelButton"
|
||||||
|
data-bind="
|
||||||
|
click: $data.onCloseTabButtonClick,
|
||||||
|
event: { keypress: onKeyPressClose },
|
||||||
|
attr: { tabindex: $data.closeButtonTabIndex },
|
||||||
|
visible: $data.isActive() || $data.isMouseOver()"
|
||||||
|
title="Close"
|
||||||
|
>
|
||||||
|
<span class="tabIcon close-Icon" data-bind="visible: $data.isActive() || $data.isMouseOver()">
|
||||||
|
<img src="../../../images/close-black.svg" title="Close" alt="Close" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<!-- /ko -->
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<!-- Tabs -- End -->
|
||||||
|
|
||||||
|
<!-- Tabs Panes -- Start -->
|
||||||
|
<div class="tabPanesContainer">
|
||||||
|
<!-- ko foreach: openedTabs -->
|
||||||
|
<div class="tabs-container" data-bind="visible: $data.isActive">
|
||||||
|
<!-- ko if: $data.tabKind === 0 -->
|
||||||
|
<documents-tab params="{data: $data}"></documents-tab>
|
||||||
|
<!-- /ko -->
|
||||||
|
|
||||||
|
<!-- ko if: $data.tabKind === 1 -->
|
||||||
|
<settings-tab params="{data: $data}"></settings-tab>
|
||||||
|
<!-- /ko -->
|
||||||
|
|
||||||
|
<!-- ko if: $data.tabKind === 2 -->
|
||||||
|
<stored-procedure-tab params="{data: $data}"></stored-procedure-tab>
|
||||||
|
<!-- /ko -->
|
||||||
|
|
||||||
|
<!-- ko if: $data.tabKind === 3 -->
|
||||||
|
<user-defined-function-tab params="{data: $data}"></user-defined-function-tab>
|
||||||
|
<!-- /ko -->
|
||||||
|
|
||||||
|
<!-- ko if: $data.tabKind === 4 -->
|
||||||
|
<trigger-tab params="{data: $data}"></trigger-tab>
|
||||||
|
<!-- /ko -->
|
||||||
|
|
||||||
|
<!-- ko if: $data.tabKind === 5 -->
|
||||||
|
<query-tab params="{data: $data}"></query-tab>
|
||||||
|
<!-- /ko -->
|
||||||
|
|
||||||
|
<!-- ko if: $data.tabKind === 6 -->
|
||||||
|
<graph-tab params="{data: $data}"></graph-tab>
|
||||||
|
<!-- /ko -->
|
||||||
|
|
||||||
|
<!-- ko if: $data.tabKind === 9 -->
|
||||||
|
<tables-query-tab class="flexContainer" params="{data: $data}"></tables-query-tab>
|
||||||
|
<!-- /ko -->
|
||||||
|
|
||||||
|
<!-- ko if: $data.tabKind === 10 -->
|
||||||
|
<mongo-shell-tab params="{data: $data}"></mongo-shell-tab>
|
||||||
|
<!-- /ko -->
|
||||||
|
|
||||||
|
<!-- ko if: $data.tabKind === 11 -->
|
||||||
|
<database-settings-tab params="{data: $data}"></database-settings-tab>
|
||||||
|
<!-- /ko -->
|
||||||
|
|
||||||
|
<!-- ko if: $data.tabKind === 12 -->
|
||||||
|
<conflicts-tab params="{data: $data}"></conflicts-tab>
|
||||||
|
<!-- /ko -->
|
||||||
|
|
||||||
|
<!-- ko if: $data.tabKind === 14 -->
|
||||||
|
<terminal-tab params="{data: $data}"></terminal-tab>
|
||||||
|
<!-- /ko -->
|
||||||
|
|
||||||
|
<!-- ko if: $data.tabKind === 15 -->
|
||||||
|
<notebookv2-tab params="{data: $data}"></notebookv2-tab>
|
||||||
|
<!-- /ko -->
|
||||||
|
|
||||||
|
<!-- ko if: $data.tabKind === 16 -->
|
||||||
|
<spark-master-tab params="{data: $data}"></spark-master-tab>
|
||||||
|
<!-- /ko -->
|
||||||
|
|
||||||
|
<!-- ko if: $data.tabKind === 17 -->
|
||||||
|
<gallery-tab params="{data: $data}"></gallery-tab>
|
||||||
|
<!-- /ko -->
|
||||||
|
|
||||||
|
<!-- ko if: $data.tabKind === 18 -->
|
||||||
|
<notebook-viewer-tab params="{data: $data}"></notebook-viewer-tab>
|
||||||
|
<!-- /ko -->
|
||||||
|
</div>
|
||||||
|
<!-- /ko -->
|
||||||
|
</div>
|
||||||
|
<!-- Tabs Panes - End -->
|
||||||
|
</div>
|
138
src/Explorer/Tabs/TabsManager.test.ts
Normal file
138
src/Explorer/Tabs/TabsManager.test.ts
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
import * as ko from "knockout";
|
||||||
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
|
import { CollectionStub, DatabaseStub } from "../../Explorer/OpenActionsStubs";
|
||||||
|
import { DataAccessUtility } from "../../Platform/Portal/DataAccessUtility";
|
||||||
|
import { TabsManager } from "./TabsManager";
|
||||||
|
import DocumentClientUtilityBase from "../../Common/DocumentClientUtilityBase";
|
||||||
|
import DocumentsTab from "./DocumentsTab";
|
||||||
|
import Explorer from "../Explorer";
|
||||||
|
import QueryTab from "./QueryTab";
|
||||||
|
|
||||||
|
describe("Tabs manager tests", () => {
|
||||||
|
let tabsManager: TabsManager;
|
||||||
|
let explorer: ViewModels.Explorer;
|
||||||
|
let database: ViewModels.Database;
|
||||||
|
let collection: ViewModels.Collection;
|
||||||
|
let queryTab: QueryTab;
|
||||||
|
let documentsTab: DocumentsTab;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
explorer = new Explorer({ documentClientUtility: undefined, notificationsClient: undefined, isEmulator: false });
|
||||||
|
explorer.databaseAccount = ko.observable<ViewModels.DatabaseAccount>({
|
||||||
|
id: "test",
|
||||||
|
name: "test",
|
||||||
|
location: "",
|
||||||
|
type: "",
|
||||||
|
kind: "",
|
||||||
|
tags: "",
|
||||||
|
properties: undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
database = new DatabaseStub({
|
||||||
|
container: explorer,
|
||||||
|
id: ko.observable<string>("test"),
|
||||||
|
isDatabaseShared: () => false
|
||||||
|
});
|
||||||
|
database.isDatabaseExpanded = ko.observable<boolean>(true);
|
||||||
|
database.selectedSubnodeKind = ko.observable<ViewModels.CollectionTabKind>();
|
||||||
|
|
||||||
|
collection = new CollectionStub({
|
||||||
|
container: explorer,
|
||||||
|
databaseId: "test",
|
||||||
|
id: ko.observable<string>("test")
|
||||||
|
});
|
||||||
|
collection.getDatabase = (): ViewModels.Database => database;
|
||||||
|
collection.isCollectionExpanded = ko.observable<boolean>(true);
|
||||||
|
collection.selectedSubnodeKind = ko.observable<ViewModels.CollectionTabKind>();
|
||||||
|
|
||||||
|
queryTab = new QueryTab({
|
||||||
|
tabKind: ViewModels.CollectionTabKind.Query,
|
||||||
|
collection,
|
||||||
|
database,
|
||||||
|
title: "",
|
||||||
|
tabPath: "",
|
||||||
|
documentClientUtility: explorer.documentClientUtility,
|
||||||
|
selfLink: "",
|
||||||
|
isActive: ko.observable<boolean>(false),
|
||||||
|
hashLocation: "",
|
||||||
|
onUpdateTabsButtons: undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
documentsTab = new DocumentsTab({
|
||||||
|
partitionKey: undefined,
|
||||||
|
documentIds: ko.observableArray<ViewModels.DocumentId>(),
|
||||||
|
tabKind: ViewModels.CollectionTabKind.Documents,
|
||||||
|
collection,
|
||||||
|
title: "",
|
||||||
|
tabPath: "",
|
||||||
|
documentClientUtility: new DocumentClientUtilityBase(new DataAccessUtility()),
|
||||||
|
selfLink: "",
|
||||||
|
hashLocation: "",
|
||||||
|
isActive: ko.observable<boolean>(false),
|
||||||
|
onUpdateTabsButtons: undefined
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tabsManager = new TabsManager();
|
||||||
|
explorer.tabsManager = tabsManager;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("open new tabs", () => {
|
||||||
|
tabsManager.activateNewTab(queryTab);
|
||||||
|
expect(tabsManager.openedTabs().length).toBe(1);
|
||||||
|
expect(tabsManager.openedTabs()[0]).toEqual(queryTab);
|
||||||
|
expect(tabsManager.activeTab()).toEqual(queryTab);
|
||||||
|
expect(queryTab.isActive()).toBe(true);
|
||||||
|
|
||||||
|
tabsManager.activateNewTab(documentsTab);
|
||||||
|
expect(tabsManager.openedTabs().length).toBe(2);
|
||||||
|
expect(tabsManager.openedTabs()[1]).toEqual(documentsTab);
|
||||||
|
expect(tabsManager.activeTab()).toEqual(documentsTab);
|
||||||
|
expect(queryTab.isActive()).toBe(false);
|
||||||
|
expect(documentsTab.isActive()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("open existing tabs", () => {
|
||||||
|
tabsManager.activateNewTab(queryTab);
|
||||||
|
tabsManager.activateNewTab(documentsTab);
|
||||||
|
tabsManager.activateTab(queryTab);
|
||||||
|
expect(tabsManager.openedTabs().length).toBe(2);
|
||||||
|
expect(tabsManager.activeTab()).toEqual(queryTab);
|
||||||
|
expect(queryTab.isActive()).toBe(true);
|
||||||
|
expect(documentsTab.isActive()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("get tabs", () => {
|
||||||
|
tabsManager.activateNewTab(queryTab);
|
||||||
|
tabsManager.activateNewTab(documentsTab);
|
||||||
|
|
||||||
|
const queryTabs: ViewModels.Tab[] = tabsManager.getTabs(ViewModels.CollectionTabKind.Query);
|
||||||
|
expect(queryTabs.length).toBe(1);
|
||||||
|
expect(queryTabs[0]).toEqual(queryTab);
|
||||||
|
|
||||||
|
const documentsTabs: ViewModels.Tab[] = tabsManager.getTabs(
|
||||||
|
ViewModels.CollectionTabKind.Documents,
|
||||||
|
(tab: ViewModels.Tab) => tab.tabId === documentsTab.tabId
|
||||||
|
);
|
||||||
|
expect(documentsTabs.length).toBe(1);
|
||||||
|
expect(documentsTabs[0]).toEqual(documentsTab);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("close tabs", () => {
|
||||||
|
tabsManager.activateNewTab(queryTab);
|
||||||
|
tabsManager.activateNewTab(documentsTab);
|
||||||
|
|
||||||
|
tabsManager.closeTab(documentsTab.tabId, explorer);
|
||||||
|
expect(tabsManager.openedTabs().length).toBe(1);
|
||||||
|
expect(tabsManager.openedTabs()[0]).toEqual(queryTab);
|
||||||
|
expect(tabsManager.activeTab()).toEqual(queryTab);
|
||||||
|
expect(queryTab.isActive()).toBe(true);
|
||||||
|
expect(documentsTab.isActive()).toBe(false);
|
||||||
|
|
||||||
|
tabsManager.closeTabsByComparator((tab: ViewModels.Tab) => tab.tabId === queryTab.tabId);
|
||||||
|
expect(tabsManager.openedTabs().length).toBe(0);
|
||||||
|
expect(tabsManager.activeTab()).toEqual(undefined);
|
||||||
|
expect(queryTab.isActive()).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
96
src/Explorer/Tabs/TabsManager.ts
Normal file
96
src/Explorer/Tabs/TabsManager.ts
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
import * as ko from "knockout";
|
||||||
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
|
import TabsManagerTemplate from "./TabsManager.html";
|
||||||
|
|
||||||
|
export class TabsManager {
|
||||||
|
public openedTabs: ko.ObservableArray<ViewModels.Tab>;
|
||||||
|
public activeTab: ko.Observable<ViewModels.Tab>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.openedTabs = ko.observableArray<ViewModels.Tab>([]);
|
||||||
|
this.activeTab = ko.observable<ViewModels.Tab>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public activateNewTab(tab: ViewModels.Tab): void {
|
||||||
|
this.openedTabs.push(tab);
|
||||||
|
this.activateTab(tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
public activateTab(tab: ViewModels.Tab): void {
|
||||||
|
this.activeTab() && this.activeTab().isActive(false);
|
||||||
|
tab.isActive(true);
|
||||||
|
this.activeTab(tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getTabs(
|
||||||
|
tabKind: ViewModels.CollectionTabKind,
|
||||||
|
comparator?: (tab: ViewModels.Tab) => boolean
|
||||||
|
): ViewModels.Tab[] {
|
||||||
|
return this.openedTabs().filter((openedTab: ViewModels.Tab) => {
|
||||||
|
return openedTab.tabKind === tabKind && (!comparator || comparator(openedTab));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public refreshActiveTab(comparator: (tab: ViewModels.Tab) => boolean): void {
|
||||||
|
// ensures that the tab selects/highlights the right node based on resource tree expand/collapse state
|
||||||
|
this.openedTabs().forEach((tab: ViewModels.Tab) => {
|
||||||
|
if (comparator(tab) && tab.isActive()) {
|
||||||
|
tab.onActivate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeTabById(tabId: string): void {
|
||||||
|
this.openedTabs.remove((tab: ViewModels.Tab) => tab.tabId === tabId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeTabByComparator(comparator: (tab: ViewModels.Tab) => boolean): void {
|
||||||
|
this.openedTabs.remove((tab: ViewModels.Tab) => comparator(tab));
|
||||||
|
}
|
||||||
|
|
||||||
|
public closeTabsByComparator(comparator: (tab: ViewModels.Tab) => boolean): void {
|
||||||
|
this.activeTab() && this.activeTab().isActive(false);
|
||||||
|
this.activeTab(undefined);
|
||||||
|
this.openedTabs().forEach((tab: ViewModels.Tab) => {
|
||||||
|
if (comparator(tab)) {
|
||||||
|
tab.onCloseTabButtonClick();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public closeTabs(): void {
|
||||||
|
this.openedTabs([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public closeTab(tabId: string, explorer: ViewModels.Explorer): void {
|
||||||
|
const tabIndex: number = this.openedTabs().findIndex((tab: ViewModels.Tab) => tab.tabId === tabId);
|
||||||
|
if (tabIndex !== -1) {
|
||||||
|
const tabToActive: ViewModels.Tab = this.openedTabs()[tabIndex + 1] || this.openedTabs()[tabIndex - 1];
|
||||||
|
this.openedTabs()[tabIndex].isActive(false);
|
||||||
|
this.removeTabById(tabId);
|
||||||
|
if (tabToActive) {
|
||||||
|
tabToActive.isActive(true);
|
||||||
|
this.activeTab(tabToActive);
|
||||||
|
} else {
|
||||||
|
explorer.selectedNode(undefined);
|
||||||
|
explorer.onUpdateTabsButtons([]);
|
||||||
|
this.activeTab(undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public isTabActive(tabKind: ViewModels.CollectionTabKind): boolean {
|
||||||
|
return this.activeTab() && this.activeTab().tabKind === tabKind;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function TabsManagerWrapperViewModel(params: { data: TabsManager }) {
|
||||||
|
return params.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TabsManagerKOComponent(): unknown {
|
||||||
|
return {
|
||||||
|
viewModel: TabsManagerWrapperViewModel,
|
||||||
|
template: TabsManagerTemplate
|
||||||
|
};
|
||||||
|
}
|
@ -16,7 +16,10 @@ import { OfferUtils } from "../../Utils/OfferUtils";
|
|||||||
import { StartUploadMessageParams, UploadDetails, UploadDetailsRecord } from "../../workers/upload/definitions";
|
import { StartUploadMessageParams, UploadDetails, UploadDetailsRecord } from "../../workers/upload/definitions";
|
||||||
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import { CassandraAPIDataClient, CassandraTableKey, CassandraTableKeys } from "../Tables/TableDataClient";
|
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 DocumentsTab from "../Tabs/DocumentsTab";
|
||||||
import GraphTab from "../Tabs/GraphTab";
|
import GraphTab from "../Tabs/GraphTab";
|
||||||
import MongoDocumentsTab from "../Tabs/MongoDocumentsTab";
|
import MongoDocumentsTab from "../Tabs/MongoDocumentsTab";
|
||||||
@ -25,8 +28,6 @@ import MongoShellTab from "../Tabs/MongoShellTab";
|
|||||||
import QueryTab from "../Tabs/QueryTab";
|
import QueryTab from "../Tabs/QueryTab";
|
||||||
import QueryTablesTab from "../Tabs/QueryTablesTab";
|
import QueryTablesTab from "../Tabs/QueryTablesTab";
|
||||||
import SettingsTab from "../Tabs/SettingsTab";
|
import SettingsTab from "../Tabs/SettingsTab";
|
||||||
import ConflictId from "./ConflictId";
|
|
||||||
import DocumentId from "./DocumentId";
|
|
||||||
import StoredProcedure from "./StoredProcedure";
|
import StoredProcedure from "./StoredProcedure";
|
||||||
import Trigger from "./Trigger";
|
import Trigger from "./Trigger";
|
||||||
import UserDefinedFunction from "./UserDefinedFunction";
|
import UserDefinedFunction from "./UserDefinedFunction";
|
||||||
@ -229,7 +230,9 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
this.expandCollection();
|
this.expandCollection();
|
||||||
}
|
}
|
||||||
this.container.onUpdateTabsButtons([]);
|
this.container.onUpdateTabsButtons([]);
|
||||||
this.refreshActiveTab();
|
this.container.tabsManager.refreshActiveTab(
|
||||||
|
(tab: ViewModels.Tab) => tab.collection && tab.collection.rid === this.rid
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public collapseCollection() {
|
public collapseCollection() {
|
||||||
@ -278,14 +281,15 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
dataExplorerArea: Constants.Areas.ResourceTree
|
dataExplorerArea: Constants.Areas.ResourceTree
|
||||||
});
|
});
|
||||||
|
|
||||||
// create documents tab if not created yet
|
const documentsTabs: DocumentsTab[] = this.container.tabsManager.getTabs(
|
||||||
const openedTabs = this.container.openedTabs();
|
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
|
if (documentsTab) {
|
||||||
.filter(tab => tab.collection && tab.collection.rid === this.rid)
|
this.container.tabsManager.activateTab(documentsTab);
|
||||||
.filter(tab => tab.tabKind === ViewModels.CollectionTabKind.Documents)[0];
|
} else {
|
||||||
|
|
||||||
if (!documentsTab) {
|
|
||||||
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
||||||
databaseAccountName: this.container.databaseAccount().name,
|
databaseAccountName: this.container.databaseAccount().name,
|
||||||
databaseName: this.databaseId,
|
databaseName: this.databaseId,
|
||||||
@ -295,6 +299,7 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
tabTitle: "Items"
|
tabTitle: "Items"
|
||||||
});
|
});
|
||||||
this.documentIds([]);
|
this.documentIds([]);
|
||||||
|
|
||||||
documentsTab = new DocumentsTab({
|
documentsTab = new DocumentsTab({
|
||||||
partitionKey: this.partitionKey,
|
partitionKey: this.partitionKey,
|
||||||
documentIds: ko.observableArray<DocumentId>([]),
|
documentIds: ko.observableArray<DocumentId>([]),
|
||||||
@ -309,14 +314,11 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
tabPath: `${this.databaseId}>${this.id()}>Documents`,
|
tabPath: `${this.databaseId}>${this.id()}>Documents`,
|
||||||
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/documents`,
|
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/documents`,
|
||||||
onLoadStartKey: startKey,
|
onLoadStartKey: startKey,
|
||||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||||
openedTabs: this.container.openedTabs()
|
|
||||||
});
|
});
|
||||||
this.container.openedTabs.push(documentsTab);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Activate
|
this.container.tabsManager.activateNewTab(documentsTab);
|
||||||
documentsTab.onTabClick();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public onConflictsClick() {
|
public onConflictsClick() {
|
||||||
@ -331,14 +333,15 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
dataExplorerArea: Constants.Areas.ResourceTree
|
dataExplorerArea: Constants.Areas.ResourceTree
|
||||||
});
|
});
|
||||||
|
|
||||||
// create documents tab if not created yet
|
const conflictsTabs: ConflictsTab[] = this.container.tabsManager.getTabs(
|
||||||
const openedTabs = this.container.openedTabs();
|
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
|
if (conflictsTab) {
|
||||||
.filter(tab => tab.collection && tab.collection.rid === this.rid)
|
this.container.tabsManager.activateTab(conflictsTab);
|
||||||
.filter(tab => tab.tabKind === ViewModels.CollectionTabKind.Conflicts)[0];
|
} else {
|
||||||
|
|
||||||
if (!conflictsTab) {
|
|
||||||
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
||||||
databaseAccountName: this.container.databaseAccount().name,
|
databaseAccountName: this.container.databaseAccount().name,
|
||||||
databaseName: this.databaseId,
|
databaseName: this.databaseId,
|
||||||
@ -348,7 +351,8 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
tabTitle: "Conflicts"
|
tabTitle: "Conflicts"
|
||||||
});
|
});
|
||||||
this.documentIds([]);
|
this.documentIds([]);
|
||||||
conflictsTab = new ConflictsTab({
|
|
||||||
|
const conflictsTab: ConflictsTab = new ConflictsTab({
|
||||||
partitionKey: this.partitionKey,
|
partitionKey: this.partitionKey,
|
||||||
conflictIds: ko.observableArray<ConflictId>([]),
|
conflictIds: ko.observableArray<ConflictId>([]),
|
||||||
tabKind: ViewModels.CollectionTabKind.Conflicts,
|
tabKind: ViewModels.CollectionTabKind.Conflicts,
|
||||||
@ -362,14 +366,11 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
tabPath: `${this.databaseId}>${this.id()}>Conflicts`,
|
tabPath: `${this.databaseId}>${this.id()}>Conflicts`,
|
||||||
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/conflicts`,
|
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/conflicts`,
|
||||||
onLoadStartKey: startKey,
|
onLoadStartKey: startKey,
|
||||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||||
openedTabs: this.container.openedTabs()
|
|
||||||
});
|
});
|
||||||
this.container.openedTabs.push(conflictsTab);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Activate
|
this.container.tabsManager.activateNewTab(conflictsTab);
|
||||||
conflictsTab.onTabClick();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public onTableEntitiesClick() {
|
public onTableEntitiesClick() {
|
||||||
@ -390,14 +391,15 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// create entities tab if not created yet
|
const queryTablesTabs: QueryTablesTab[] = this.container.tabsManager.getTabs(
|
||||||
const openedTabs = this.container.openedTabs();
|
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
|
if (queryTablesTab) {
|
||||||
.filter(tab => tab.collection && tab.collection.rid === this.rid)
|
this.container.tabsManager.activateTab(queryTablesTab);
|
||||||
.filter(tab => tab.tabKind === ViewModels.CollectionTabKind.QueryTables)[0];
|
} else {
|
||||||
|
|
||||||
if (!documentsTab) {
|
|
||||||
this.documentIds([]);
|
this.documentIds([]);
|
||||||
let title = `Entities`;
|
let title = `Entities`;
|
||||||
if (this.container.isPreferredApiCassandra()) {
|
if (this.container.isPreferredApiCassandra()) {
|
||||||
@ -411,7 +413,8 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
dataExplorerArea: Constants.Areas.Tab,
|
dataExplorerArea: Constants.Areas.Tab,
|
||||||
tabTitle: title
|
tabTitle: title
|
||||||
});
|
});
|
||||||
documentsTab = new QueryTablesTab({
|
|
||||||
|
queryTablesTab = new QueryTablesTab({
|
||||||
tabKind: ViewModels.CollectionTabKind.QueryTables,
|
tabKind: ViewModels.CollectionTabKind.QueryTables,
|
||||||
title: title,
|
title: title,
|
||||||
tabPath: "",
|
tabPath: "",
|
||||||
@ -423,14 +426,11 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/entities`,
|
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/entities`,
|
||||||
isActive: ko.observable(false),
|
isActive: ko.observable(false),
|
||||||
onLoadStartKey: startKey,
|
onLoadStartKey: startKey,
|
||||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||||
openedTabs: this.container.openedTabs()
|
|
||||||
});
|
});
|
||||||
this.container.openedTabs.push(documentsTab);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Activate
|
this.container.tabsManager.activateNewTab(queryTablesTab);
|
||||||
documentsTab.onTabClick();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public onGraphDocumentsClick() {
|
public onGraphDocumentsClick() {
|
||||||
@ -445,55 +445,50 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
dataExplorerArea: Constants.Areas.ResourceTree
|
dataExplorerArea: Constants.Areas.ResourceTree
|
||||||
});
|
});
|
||||||
|
|
||||||
// create documents tab if not created yet
|
const graphTabs: GraphTab[] = this.container.tabsManager.getTabs(
|
||||||
const openedTabs = this.container.openedTabs();
|
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
|
if (graphTab) {
|
||||||
.filter(tab => tab.collection && tab.collection.rid === this.rid)
|
this.container.tabsManager.activateTab(graphTab);
|
||||||
.filter(tab => tab.tabKind === ViewModels.CollectionTabKind.Graph)[0];
|
} else {
|
||||||
|
|
||||||
if (!documentsTab) {
|
|
||||||
this.documentIds([]);
|
this.documentIds([]);
|
||||||
documentsTab = this._createGraphTab("Graph");
|
const title = "Graph";
|
||||||
this.container.openedTabs.push(documentsTab);
|
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 = () => {
|
public onMongoDBDocumentsClick = () => {
|
||||||
this.container.selectedNode(this);
|
this.container.selectedNode(this);
|
||||||
this.selectedSubnodeKind(ViewModels.CollectionTabKind.Documents);
|
this.selectedSubnodeKind(ViewModels.CollectionTabKind.Documents);
|
||||||
@ -506,14 +501,15 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
dataExplorerArea: Constants.Areas.ResourceTree
|
dataExplorerArea: Constants.Areas.ResourceTree
|
||||||
});
|
});
|
||||||
|
|
||||||
// create documents tab if not created yet
|
const mongoDocumentsTabs: MongoDocumentsTab[] = this.container.tabsManager.getTabs(
|
||||||
const openedTabs = this.container.openedTabs();
|
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
|
if (mongoDocumentsTab) {
|
||||||
.filter(tab => tab.collection && tab.collection && tab.collection.rid === this.rid)
|
this.container.tabsManager.activateTab(mongoDocumentsTab);
|
||||||
.filter(tab => tab.tabKind === ViewModels.CollectionTabKind.Documents)[0];
|
} else {
|
||||||
|
|
||||||
if (!documentsTab) {
|
|
||||||
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
||||||
databaseAccountName: this.container.databaseAccount().name,
|
databaseAccountName: this.container.databaseAccount().name,
|
||||||
databaseName: this.databaseId,
|
databaseName: this.databaseId,
|
||||||
@ -523,7 +519,8 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
tabTitle: "Documents"
|
tabTitle: "Documents"
|
||||||
});
|
});
|
||||||
this.documentIds([]);
|
this.documentIds([]);
|
||||||
documentsTab = new MongoDocumentsTab({
|
|
||||||
|
mongoDocumentsTab = new MongoDocumentsTab({
|
||||||
partitionKey: this.partitionKey,
|
partitionKey: this.partitionKey,
|
||||||
documentIds: this.documentIds,
|
documentIds: this.documentIds,
|
||||||
tabKind: ViewModels.CollectionTabKind.Documents,
|
tabKind: ViewModels.CollectionTabKind.Documents,
|
||||||
@ -537,14 +534,10 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/mongoDocuments`,
|
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/mongoDocuments`,
|
||||||
isActive: ko.observable(false),
|
isActive: ko.observable(false),
|
||||||
onLoadStartKey: startKey,
|
onLoadStartKey: startKey,
|
||||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||||
openedTabs: this.container.openedTabs()
|
|
||||||
});
|
});
|
||||||
this.container.openedTabs.push(documentsTab);
|
this.container.tabsManager.activateNewTab(mongoDocumentsTab);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Activate
|
|
||||||
documentsTab.onTabClick();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public onSettingsClick = () => {
|
public onSettingsClick = () => {
|
||||||
@ -559,14 +552,15 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
dataExplorerArea: Constants.Areas.ResourceTree
|
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 tabTitle = "Scale & Settings";
|
||||||
const pendingNotificationsPromise: Q.Promise<DataModels.Notification> = this._getPendingThroughputSplitNotification();
|
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) {
|
if (!settingsTab) {
|
||||||
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
||||||
databaseAccountName: this.container.databaseAccount().name,
|
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`,
|
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/settings`,
|
||||||
isActive: ko.observable(false),
|
isActive: ko.observable(false),
|
||||||
onLoadStartKey: startKey,
|
onLoadStartKey: startKey,
|
||||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||||
openedTabs: this.container.openedTabs()
|
|
||||||
});
|
});
|
||||||
(settingsTab as ViewModels.SettingsTab).pendingNotification(pendingNotification);
|
this.container.tabsManager.activateNewTab(settingsTab);
|
||||||
this.container.openedTabs.push(settingsTab);
|
settingsTab.pendingNotification(pendingNotification);
|
||||||
settingsTab.onTabClick(); // Activate
|
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
TelemetryProcessor.traceFailure(
|
TelemetryProcessor.traceFailure(
|
||||||
@ -623,12 +615,12 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
} else {
|
} else {
|
||||||
pendingNotificationsPromise.then(
|
pendingNotificationsPromise.then(
|
||||||
(pendingNotification: DataModels.Notification) => {
|
(pendingNotification: DataModels.Notification) => {
|
||||||
(settingsTab as ViewModels.SettingsTab).pendingNotification(pendingNotification);
|
settingsTab.pendingNotification(pendingNotification);
|
||||||
settingsTab.onTabClick();
|
this.container.tabsManager.activateTab(settingsTab);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
(settingsTab as ViewModels.SettingsTab).pendingNotification(undefined);
|
settingsTab.pendingNotification(undefined);
|
||||||
settingsTab.onTabClick();
|
this.container.tabsManager.activateTab(settingsTab);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -738,9 +730,7 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
|
|
||||||
public onNewQueryClick(source: any, event: MouseEvent, queryText?: string) {
|
public onNewQueryClick(source: any, event: MouseEvent, queryText?: string) {
|
||||||
const collection: ViewModels.Collection = source.collection || source;
|
const collection: ViewModels.Collection = source.collection || source;
|
||||||
const explorer: ViewModels.Explorer = source.container;
|
const id = this.container.tabsManager.getTabs(ViewModels.CollectionTabKind.Query).length + 1;
|
||||||
const openedTabs = explorer.openedTabs();
|
|
||||||
const id = openedTabs.filter(t => t.tabKind === ViewModels.CollectionTabKind.Query).length + 1;
|
|
||||||
const title = "Query " + id;
|
const title = "Query " + id;
|
||||||
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
||||||
databaseAccountName: this.container.databaseAccount().name,
|
databaseAccountName: this.container.databaseAccount().name,
|
||||||
@ -751,7 +741,7 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
tabTitle: title
|
tabTitle: title
|
||||||
});
|
});
|
||||||
|
|
||||||
let queryTab: ViewModels.Tab = new QueryTab({
|
const queryTab: QueryTab = new QueryTab({
|
||||||
tabKind: ViewModels.CollectionTabKind.Query,
|
tabKind: ViewModels.CollectionTabKind.Query,
|
||||||
title: title,
|
title: title,
|
||||||
tabPath: "",
|
tabPath: "",
|
||||||
@ -764,20 +754,15 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
queryText: queryText,
|
queryText: queryText,
|
||||||
partitionKey: collection.partitionKey,
|
partitionKey: collection.partitionKey,
|
||||||
onLoadStartKey: startKey,
|
onLoadStartKey: startKey,
|
||||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||||
openedTabs: this.container.openedTabs()
|
|
||||||
});
|
});
|
||||||
this.container.openedTabs.push(queryTab);
|
|
||||||
|
|
||||||
// Activate
|
this.container.tabsManager.activateNewTab(queryTab);
|
||||||
queryTab.onTabClick();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public onNewMongoQueryClick(source: any, event: MouseEvent, queryText?: string) {
|
public onNewMongoQueryClick(source: any, event: MouseEvent, queryText?: string) {
|
||||||
const collection: ViewModels.Collection = source.collection || source;
|
const collection: ViewModels.Collection = source.collection || source;
|
||||||
const explorer: ViewModels.Explorer = source.container;
|
const id = this.container.tabsManager.getTabs(ViewModels.CollectionTabKind.Query).length + 1;
|
||||||
const openedTabs = explorer.openedTabs();
|
|
||||||
const id = openedTabs.filter(t => t.tabKind === ViewModels.CollectionTabKind.Query).length + 1;
|
|
||||||
|
|
||||||
const title = "Query " + id;
|
const title = "Query " + id;
|
||||||
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
||||||
@ -789,7 +774,7 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
tabTitle: title
|
tabTitle: title
|
||||||
});
|
});
|
||||||
|
|
||||||
let queryTab: ViewModels.Tab = new MongoQueryTab({
|
const mongoQueryTab: MongoQueryTab = new MongoQueryTab({
|
||||||
tabKind: ViewModels.CollectionTabKind.Query,
|
tabKind: ViewModels.CollectionTabKind.Query,
|
||||||
title: title,
|
title: title,
|
||||||
tabPath: "",
|
tabPath: "",
|
||||||
@ -801,26 +786,51 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
isActive: ko.observable(false),
|
isActive: ko.observable(false),
|
||||||
partitionKey: collection.partitionKey,
|
partitionKey: collection.partitionKey,
|
||||||
onLoadStartKey: startKey,
|
onLoadStartKey: startKey,
|
||||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||||
openedTabs: this.container.openedTabs()
|
|
||||||
});
|
});
|
||||||
this.container.openedTabs.push(queryTab);
|
|
||||||
|
|
||||||
// Activate
|
this.container.tabsManager.activateNewTab(mongoQueryTab);
|
||||||
queryTab.onTabClick();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public onNewGraphClick() {
|
public onNewGraphClick() {
|
||||||
var id = this.container.openedTabs().filter(t => t.tabKind === ViewModels.CollectionTabKind.Graph).length + 1;
|
const id: number = this.container.tabsManager.getTabs(ViewModels.CollectionTabKind.Graph).length + 1;
|
||||||
var graphTab = this._createGraphTab("Graph Query " + id);
|
const title: string = "Graph Query " + id;
|
||||||
this.container.openedTabs.push(graphTab);
|
|
||||||
// Activate
|
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
||||||
graphTab.onTabClick();
|
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() {
|
public onNewMongoShellClick() {
|
||||||
var id = this.container.openedTabs().filter(t => t.tabKind === ViewModels.CollectionTabKind.MongoShell).length + 1;
|
const id = this.container.tabsManager.getTabs(ViewModels.CollectionTabKind.MongoShell).length + 1;
|
||||||
var mongoShellTab = new MongoShellTab({
|
const mongoShellTab: MongoShellTab = new MongoShellTab({
|
||||||
tabKind: ViewModels.CollectionTabKind.MongoShell,
|
tabKind: ViewModels.CollectionTabKind.MongoShell,
|
||||||
title: "Shell " + id,
|
title: "Shell " + id,
|
||||||
tabPath: "",
|
tabPath: "",
|
||||||
@ -830,14 +840,10 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/mongoShell`,
|
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/mongoShell`,
|
||||||
selfLink: this.self,
|
selfLink: this.self,
|
||||||
isActive: ko.observable(false),
|
isActive: ko.observable(false),
|
||||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||||
openedTabs: this.container.openedTabs()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.container.openedTabs.push(mongoShellTab);
|
this.container.tabsManager.activateNewTab(mongoShellTab);
|
||||||
|
|
||||||
// Activate
|
|
||||||
mongoShellTab.onTabClick();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public onNewStoredProcedureClick(source: ViewModels.Collection, event: MouseEvent) {
|
public onNewStoredProcedureClick(source: ViewModels.Collection, event: MouseEvent) {
|
||||||
@ -898,7 +904,9 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
} else {
|
} else {
|
||||||
this.expandStoredProcedures();
|
this.expandStoredProcedures();
|
||||||
}
|
}
|
||||||
this.refreshActiveTab();
|
this.container.tabsManager.refreshActiveTab(
|
||||||
|
(tab: ViewModels.Tab) => tab.collection && tab.collection.rid === this.rid
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public expandStoredProcedures() {
|
public expandStoredProcedures() {
|
||||||
@ -955,7 +963,9 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
} else {
|
} else {
|
||||||
this.expandUserDefinedFunctions();
|
this.expandUserDefinedFunctions();
|
||||||
}
|
}
|
||||||
this.refreshActiveTab();
|
this.container.tabsManager.refreshActiveTab(
|
||||||
|
(tab: ViewModels.Tab) => tab.collection && tab.collection.rid === this.rid
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public expandUserDefinedFunctions() {
|
public expandUserDefinedFunctions() {
|
||||||
@ -1012,7 +1022,9 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
} else {
|
} else {
|
||||||
this.expandTriggers();
|
this.expandTriggers();
|
||||||
}
|
}
|
||||||
this.refreshActiveTab();
|
this.container.tabsManager.refreshActiveTab(
|
||||||
|
(tab: ViewModels.Tab) => tab.collection && tab.collection.rid === this.rid
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public expandTriggers() {
|
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 {
|
protected _getOfferForCollection(offers: DataModels.Offer[], collection: DataModels.Collection): DataModels.Offer {
|
||||||
return _.find(offers, (offer: DataModels.Offer) => offer.resource.indexOf(collection._rid) >= 0);
|
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
|
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 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) {
|
if (!settingsTab) {
|
||||||
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
||||||
databaseAccountName: this.container.databaseAccount().name,
|
databaseAccountName: this.container.databaseAccount().name,
|
||||||
@ -78,12 +78,11 @@ export default class Database implements ViewModels.Database {
|
|||||||
selfLink: this.self,
|
selfLink: this.self,
|
||||||
isActive: ko.observable(false),
|
isActive: ko.observable(false),
|
||||||
onLoadStartKey: startKey,
|
onLoadStartKey: startKey,
|
||||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||||
openedTabs: this.container.openedTabs()
|
|
||||||
});
|
});
|
||||||
(settingsTab as ViewModels.DatabaseSettingsTab).pendingNotification(pendingNotification);
|
|
||||||
this.container.openedTabs.push(settingsTab);
|
settingsTab.pendingNotification(pendingNotification);
|
||||||
settingsTab.onTabClick(); // Activate
|
this.container.tabsManager.activateNewTab(settingsTab);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
TelemetryProcessor.traceFailure(
|
TelemetryProcessor.traceFailure(
|
||||||
@ -109,12 +108,12 @@ export default class Database implements ViewModels.Database {
|
|||||||
} else {
|
} else {
|
||||||
pendingNotificationsPromise.then(
|
pendingNotificationsPromise.then(
|
||||||
(pendingNotification: DataModels.Notification) => {
|
(pendingNotification: DataModels.Notification) => {
|
||||||
(settingsTab as ViewModels.DatabaseSettingsTab).pendingNotification(pendingNotification);
|
settingsTab.pendingNotification(pendingNotification);
|
||||||
settingsTab.onTabClick();
|
this.container.tabsManager.activateTab(settingsTab);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
(settingsTab as ViewModels.DatabaseSettingsTab).pendingNotification(undefined);
|
settingsTab.pendingNotification(undefined);
|
||||||
settingsTab.onTabClick();
|
this.container.tabsManager.activateTab(settingsTab);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -221,7 +220,9 @@ export default class Database implements ViewModels.Database {
|
|||||||
this.expandDatabase();
|
this.expandDatabase();
|
||||||
}
|
}
|
||||||
this.container.onUpdateTabsButtons([]);
|
this.container.onUpdateTabsButtons([]);
|
||||||
this.refreshTabSelectedState();
|
this.container.tabsManager.refreshActiveTab(
|
||||||
|
(tab: ViewModels.Tab) => tab.collection && tab.collection.getDatabase().rid === this.rid
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public expandDatabase() {
|
public expandDatabase() {
|
||||||
@ -286,18 +287,6 @@ export default class Database implements ViewModels.Database {
|
|||||||
database.container.addCollectionPane.open();
|
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 {
|
public findCollectionWithId(collectionId: string): ViewModels.Collection {
|
||||||
return _.find(this.collections(), (collection: ViewModels.Collection) => collection.id() === collectionId);
|
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) {
|
public onNewQueryClick(source: any, event: MouseEvent, queryText?: string) {
|
||||||
const collection: ViewModels.Collection = source.collection || source;
|
const collection: ViewModels.Collection = source.collection || source;
|
||||||
const explorer: ViewModels.Explorer = source.container;
|
const id = this.container.tabsManager.getTabs(ViewModels.CollectionTabKind.Query).length + 1;
|
||||||
const openedTabs = explorer.openedTabs();
|
|
||||||
const id = openedTabs.filter(t => t.tabKind === ViewModels.CollectionTabKind.Query).length + 1;
|
|
||||||
const title = "Query " + id;
|
const title = "Query " + id;
|
||||||
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
||||||
databaseAccountName: this.container.databaseAccount().name,
|
databaseAccountName: this.container.databaseAccount().name,
|
||||||
@ -101,7 +86,7 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
|
|||||||
tabTitle: title
|
tabTitle: title
|
||||||
});
|
});
|
||||||
|
|
||||||
let queryTab: ViewModels.Tab = new QueryTab({
|
const queryTab: QueryTab = new QueryTab({
|
||||||
tabKind: ViewModels.CollectionTabKind.Query,
|
tabKind: ViewModels.CollectionTabKind.Query,
|
||||||
title: title,
|
title: title,
|
||||||
tabPath: "",
|
tabPath: "",
|
||||||
@ -115,13 +100,10 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
|
|||||||
partitionKey: collection.partitionKey,
|
partitionKey: collection.partitionKey,
|
||||||
resourceTokenPartitionKey: this.container.resourceTokenPartitionKey(),
|
resourceTokenPartitionKey: this.container.resourceTokenPartitionKey(),
|
||||||
onLoadStartKey: startKey,
|
onLoadStartKey: startKey,
|
||||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||||
openedTabs: this.container.openedTabs()
|
|
||||||
});
|
});
|
||||||
this.container.openedTabs.push(queryTab);
|
|
||||||
|
|
||||||
// Activate
|
this.container.tabsManager.activateNewTab(queryTab);
|
||||||
queryTab.onTabClick();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public onDocumentDBDocumentsClick() {
|
public onDocumentDBDocumentsClick() {
|
||||||
@ -136,14 +118,15 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
|
|||||||
dataExplorerArea: Constants.Areas.ResourceTree
|
dataExplorerArea: Constants.Areas.ResourceTree
|
||||||
});
|
});
|
||||||
|
|
||||||
// create documents tab if not created yet
|
const documentsTabs: DocumentsTab[] = this.container.tabsManager.getTabs(
|
||||||
const openedTabs = this.container.openedTabs();
|
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
|
if (documentsTab) {
|
||||||
.filter(tab => tab.collection && tab.collection.rid === this.rid)
|
this.container.tabsManager.activateTab(documentsTab);
|
||||||
.filter(tab => tab.tabKind === ViewModels.CollectionTabKind.Documents)[0];
|
} else {
|
||||||
|
|
||||||
if (!documentsTab) {
|
|
||||||
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
const startKey: number = TelemetryProcessor.traceStart(Action.Tab, {
|
||||||
databaseAccountName: this.container.databaseAccount() && this.container.databaseAccount().name,
|
databaseAccountName: this.container.databaseAccount() && this.container.databaseAccount().name,
|
||||||
databaseName: this.databaseId,
|
databaseName: this.databaseId,
|
||||||
@ -152,6 +135,7 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
|
|||||||
dataExplorerArea: Constants.Areas.Tab,
|
dataExplorerArea: Constants.Areas.Tab,
|
||||||
tabTitle: "Items"
|
tabTitle: "Items"
|
||||||
});
|
});
|
||||||
|
|
||||||
documentsTab = new DocumentsTab({
|
documentsTab = new DocumentsTab({
|
||||||
partitionKey: this.partitionKey,
|
partitionKey: this.partitionKey,
|
||||||
resourceTokenPartitionKey: this.container.resourceTokenPartitionKey(),
|
resourceTokenPartitionKey: this.container.resourceTokenPartitionKey(),
|
||||||
@ -167,14 +151,11 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
|
|||||||
tabPath: `${this.databaseId}>${this.id()}>Documents`,
|
tabPath: `${this.databaseId}>${this.id()}>Documents`,
|
||||||
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/documents`,
|
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/documents`,
|
||||||
onLoadStartKey: startKey,
|
onLoadStartKey: startKey,
|
||||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||||
openedTabs: this.container.openedTabs()
|
|
||||||
});
|
});
|
||||||
this.container.openedTabs.push(documentsTab);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Activate
|
this.container.tabsManager.activateNewTab(documentsTab);
|
||||||
documentsTab.onTabClick();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getDatabase(): ViewModels.Database {
|
public getDatabase(): ViewModels.Database {
|
||||||
|
@ -46,7 +46,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||||||
this.parameters = ko.observable(Date.now());
|
this.parameters = ko.observable(Date.now());
|
||||||
|
|
||||||
this.container.selectedNode.subscribe((newValue: any) => this.triggerRender());
|
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.container.isNotebookEnabled.subscribe(newValue => this.triggerRender());
|
||||||
|
|
||||||
this.koSubsDatabaseIdMap = new ArrayHashMap();
|
this.koSubsDatabaseIdMap = new ArrayHashMap();
|
||||||
@ -171,7 +171,9 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||||||
}
|
}
|
||||||
database.selectDatabase();
|
database.selectDatabase();
|
||||||
this.container.onUpdateTabsButtons([]);
|
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)
|
onContextMenuOpen: () => this.container.selectedNode(database)
|
||||||
};
|
};
|
||||||
@ -268,7 +270,9 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||||||
// Rewritten version of expandCollapseCollection
|
// Rewritten version of expandCollapseCollection
|
||||||
this.container.selectedNode(collection);
|
this.container.selectedNode(collection);
|
||||||
this.container.onUpdateTabsButtons([]);
|
this.container.onUpdateTabsButtons([]);
|
||||||
collection.refreshActiveTab();
|
this.container.tabsManager.refreshActiveTab(
|
||||||
|
(tab: ViewModels.Tab) => tab.collection && tab.collection.rid === collection.rid
|
||||||
|
);
|
||||||
},
|
},
|
||||||
onExpanded: () => {
|
onExpanded: () => {
|
||||||
if (ResourceTreeAdapter.showScriptNodes(this.container)) {
|
if (ResourceTreeAdapter.showScriptNodes(this.container)) {
|
||||||
@ -294,7 +298,9 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||||||
})),
|
})),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
collection.selectedSubnodeKind(ViewModels.CollectionTabKind.StoredProcedures);
|
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: () => {
|
onClick: () => {
|
||||||
collection.selectedSubnodeKind(ViewModels.CollectionTabKind.UserDefinedFunctions);
|
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: () => {
|
onClick: () => {
|
||||||
collection.selectedSubnodeKind(ViewModels.CollectionTabKind.Triggers);
|
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",
|
className: "notebookHeader galleryHeader",
|
||||||
onClick: () => this.container.openGallery(),
|
onClick: () => this.container.openGallery(),
|
||||||
isSelected: () => {
|
isSelected: () => {
|
||||||
const activeTab = this.container.findActiveTab();
|
const activeTab = this.container.tabsManager.activeTab();
|
||||||
return activeTab && activeTab.tabKind === ViewModels.CollectionTabKind.Gallery;
|
return activeTab && activeTab.tabKind === ViewModels.CollectionTabKind.Gallery;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -517,7 +527,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||||||
className: "notebookHeader",
|
className: "notebookHeader",
|
||||||
onClick: () => onFileClick(item),
|
onClick: () => onFileClick(item),
|
||||||
isSelected: () => {
|
isSelected: () => {
|
||||||
const activeTab = this.container.findActiveTab();
|
const activeTab = this.container.tabsManager.activeTab();
|
||||||
return (
|
return (
|
||||||
activeTab &&
|
activeTab &&
|
||||||
activeTab.tabKind === ViewModels.CollectionTabKind.NotebookV2 &&
|
activeTab.tabKind === ViewModels.CollectionTabKind.NotebookV2 &&
|
||||||
@ -634,7 +644,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
isSelected: () => {
|
isSelected: () => {
|
||||||
const activeTab = this.container.findActiveTab();
|
const activeTab = this.container.tabsManager.activeTab();
|
||||||
return (
|
return (
|
||||||
activeTab &&
|
activeTab &&
|
||||||
activeTab.tabKind === ViewModels.CollectionTabKind.NotebookV2 &&
|
activeTab.tabKind === ViewModels.CollectionTabKind.NotebookV2 &&
|
||||||
@ -657,14 +667,6 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||||||
window.requestAnimationFrame(() => this.parameters(Date.now()));
|
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 {
|
private isDataNodeSelected(rid: string, nodeKind: string, subnodeKind: ViewModels.CollectionTabKind): boolean {
|
||||||
if (!this.container.selectedNode || !this.container.selectedNode()) {
|
if (!this.container.selectedNode || !this.container.selectedNode()) {
|
||||||
return false;
|
return false;
|
||||||
@ -674,7 +676,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||||||
if (subnodeKind === undefined) {
|
if (subnodeKind === undefined) {
|
||||||
return selectedNode.rid === rid && selectedNode.nodeKind === nodeKind;
|
return selectedNode.rid === rid && selectedNode.nodeKind === nodeKind;
|
||||||
} else {
|
} else {
|
||||||
const activeTab = this.getActiveTab();
|
const activeTab = this.container.tabsManager.activeTab();
|
||||||
let selectedSubnodeKind;
|
let selectedSubnodeKind;
|
||||||
if (nodeKind === "Database" && (selectedNode as ViewModels.Database).selectedSubnodeKind) {
|
if (nodeKind === "Database" && (selectedNode as ViewModels.Database).selectedSubnodeKind) {
|
||||||
selectedSubnodeKind = (selectedNode as ViewModels.Database).selectedSubnodeKind();
|
selectedSubnodeKind = (selectedNode as ViewModels.Database).selectedSubnodeKind();
|
||||||
|
@ -12,9 +12,7 @@ const createMockContainer = (): ViewModels.Explorer => {
|
|||||||
let mockContainer = {} as ViewModels.Explorer;
|
let mockContainer = {} as ViewModels.Explorer;
|
||||||
mockContainer.resourceTokenCollection = createMockCollection(mockContainer);
|
mockContainer.resourceTokenCollection = createMockCollection(mockContainer);
|
||||||
mockContainer.selectedNode = ko.observable<ViewModels.TreeNode>();
|
mockContainer.selectedNode = ko.observable<ViewModels.TreeNode>();
|
||||||
mockContainer.activeTab = ko.observable<ViewModels.Tab>();
|
|
||||||
mockContainer.mostRecentActivity = new MostRecentActivity.MostRecentActivity(mockContainer);
|
mockContainer.mostRecentActivity = new MostRecentActivity.MostRecentActivity(mockContainer);
|
||||||
mockContainer.openedTabs = ko.observableArray<ViewModels.Tab>([]);
|
|
||||||
mockContainer.onUpdateTabsButtons = () => {};
|
mockContainer.onUpdateTabsButtons = () => {};
|
||||||
|
|
||||||
return mockContainer;
|
return mockContainer;
|
||||||
|
@ -17,7 +17,8 @@ export class ResourceTreeAdapterForResourceToken implements ReactAdapter {
|
|||||||
|
|
||||||
this.container.resourceTokenCollection.subscribe((collection: ViewModels.CollectionBase) => this.triggerRender());
|
this.container.resourceTokenCollection.subscribe((collection: ViewModels.CollectionBase) => this.triggerRender());
|
||||||
this.container.selectedNode.subscribe((newValue: any) => 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();
|
this.triggerRender();
|
||||||
}
|
}
|
||||||
@ -63,7 +64,9 @@ export class ResourceTreeAdapterForResourceToken implements ReactAdapter {
|
|||||||
// Rewritten version of expandCollapseCollection
|
// Rewritten version of expandCollapseCollection
|
||||||
this.container.selectedNode(collection);
|
this.container.selectedNode(collection);
|
||||||
this.container.onUpdateTabsButtons([]);
|
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)
|
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 {
|
private isDataNodeSelected(rid: string, nodeKind: string, subnodeKind: ViewModels.CollectionTabKind): boolean {
|
||||||
if (!this.container.selectedNode || !this.container.selectedNode()) {
|
if (!this.container.selectedNode || !this.container.selectedNode()) {
|
||||||
return false;
|
return false;
|
||||||
@ -92,7 +87,7 @@ export class ResourceTreeAdapterForResourceToken implements ReactAdapter {
|
|||||||
if (subnodeKind) {
|
if (subnodeKind) {
|
||||||
return selectedNode.rid === rid && selectedNode.nodeKind === nodeKind;
|
return selectedNode.rid === rid && selectedNode.nodeKind === nodeKind;
|
||||||
} else {
|
} else {
|
||||||
const activeTab = this.getActiveTab();
|
const activeTab = this.container.tabsManager.activeTab();
|
||||||
let selectedSubnodeKind;
|
let selectedSubnodeKind;
|
||||||
if (nodeKind === "Database" && (selectedNode as ViewModels.Database).selectedSubnodeKind) {
|
if (nodeKind === "Database" && (selectedNode as ViewModels.Database).selectedSubnodeKind) {
|
||||||
selectedSubnodeKind = (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) {
|
public static create(source: ViewModels.Collection, event: MouseEvent) {
|
||||||
const openedTabs = source.container.openedTabs();
|
const id = source.container.tabsManager.getTabs(ViewModels.CollectionTabKind.StoredProcedures).length + 1;
|
||||||
const id =
|
|
||||||
openedTabs.filter((tab: ViewModels.Tab) => tab.tabKind === ViewModels.CollectionTabKind.StoredProcedures).length +
|
|
||||||
1;
|
|
||||||
const storedProcedure = <DataModels.StoredProcedure>{
|
const storedProcedure = <DataModels.StoredProcedure>{
|
||||||
id: "",
|
id: "",
|
||||||
body: sampleStoredProcedureBody
|
body: sampleStoredProcedureBody
|
||||||
};
|
};
|
||||||
let storedProcedureTab: ViewModels.Tab = new StoredProcedureTab({
|
|
||||||
|
const storedProcedureTab: StoredProcedureTab = new StoredProcedureTab({
|
||||||
resource: storedProcedure,
|
resource: storedProcedure,
|
||||||
isNew: true,
|
isNew: true,
|
||||||
tabKind: ViewModels.CollectionTabKind.StoredProcedures,
|
tabKind: ViewModels.CollectionTabKind.StoredProcedures,
|
||||||
@ -76,13 +74,10 @@ export default class StoredProcedure implements ViewModels.StoredProcedure {
|
|||||||
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(source.databaseId, source.id())}/sproc`,
|
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(source.databaseId, source.id())}/sproc`,
|
||||||
selfLink: "",
|
selfLink: "",
|
||||||
isActive: ko.observable(false),
|
isActive: ko.observable(false),
|
||||||
onUpdateTabsButtons: source.container.onUpdateTabsButtons,
|
onUpdateTabsButtons: source.container.onUpdateTabsButtons
|
||||||
openedTabs: source.container.openedTabs()
|
|
||||||
});
|
});
|
||||||
source.container.openedTabs.push(storedProcedureTab);
|
|
||||||
|
|
||||||
// Activate
|
source.container.tabsManager.activateNewTab(storedProcedureTab);
|
||||||
storedProcedureTab.onTabClick();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public select() {
|
public select() {
|
||||||
@ -98,15 +93,15 @@ export default class StoredProcedure implements ViewModels.StoredProcedure {
|
|||||||
public open = () => {
|
public open = () => {
|
||||||
this.select();
|
this.select();
|
||||||
|
|
||||||
const openedTabs = this.container.openedTabs();
|
const storedProcedureTabs: StoredProcedureTab[] = this.container.tabsManager.getTabs(
|
||||||
const storedProcedureTabsOpen: ViewModels.Tab[] =
|
ViewModels.CollectionTabKind.StoredProcedures,
|
||||||
openedTabs &&
|
(tab: ViewModels.Tab) => tab.node && tab.node.rid === this.rid
|
||||||
openedTabs.filter(
|
) as StoredProcedureTab[];
|
||||||
tab => tab.node && tab.node.rid === this.rid && tab.tabKind === ViewModels.CollectionTabKind.StoredProcedures
|
let storedProcedureTab: StoredProcedureTab = storedProcedureTabs && storedProcedureTabs[0];
|
||||||
);
|
|
||||||
let storedProcedureTab: ViewModels.Tab =
|
if (storedProcedureTab) {
|
||||||
storedProcedureTabsOpen && storedProcedureTabsOpen.length > 0 && storedProcedureTabsOpen[0];
|
this.container.tabsManager.activateTab(storedProcedureTab);
|
||||||
if (!storedProcedureTab) {
|
} else {
|
||||||
const storedProcedureData = <DataModels.StoredProcedure>{
|
const storedProcedureData = <DataModels.StoredProcedure>{
|
||||||
_rid: this.rid,
|
_rid: this.rid,
|
||||||
_self: this.self,
|
_self: this.self,
|
||||||
@ -129,14 +124,11 @@ export default class StoredProcedure implements ViewModels.StoredProcedure {
|
|||||||
)}/sprocs/${this.id()}`,
|
)}/sprocs/${this.id()}`,
|
||||||
selfLink: this.self,
|
selfLink: this.self,
|
||||||
isActive: ko.observable(false),
|
isActive: ko.observable(false),
|
||||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||||
openedTabs: this.container.openedTabs()
|
|
||||||
});
|
});
|
||||||
this.container.openedTabs.push(storedProcedureTab);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Activate
|
this.container.tabsManager.activateNewTab(storedProcedureTab);
|
||||||
storedProcedureTab.onTabClick();
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public delete() {
|
public delete() {
|
||||||
@ -153,7 +145,9 @@ export default class StoredProcedure implements ViewModels.StoredProcedure {
|
|||||||
|
|
||||||
this.container.documentClientUtility.deleteStoredProcedure(this.collection, storedProcedureData).then(
|
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);
|
this.collection.children.remove(this);
|
||||||
},
|
},
|
||||||
reason => {}
|
reason => {}
|
||||||
@ -161,7 +155,11 @@ export default class StoredProcedure implements ViewModels.StoredProcedure {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public execute(params: string[], partitionKeyValue?: string): void {
|
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);
|
sprocTab.isExecuting(true);
|
||||||
this.container &&
|
this.container &&
|
||||||
this.container.documentClientUtility
|
this.container.documentClientUtility
|
||||||
@ -184,17 +182,4 @@ export default class StoredProcedure implements ViewModels.StoredProcedure {
|
|||||||
const focusElement = document.getElementById("execute-storedproc-toggles");
|
const focusElement = document.getElementById("execute-storedproc-toggles");
|
||||||
focusElement && focusElement.focus();
|
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 Constants from "../../Common/Constants";
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
import * as DataModels from "../../Contracts/DataModels";
|
||||||
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||||
import Collection from "./Collection";
|
|
||||||
import TriggerTab from "../Tabs/TriggerTab";
|
import TriggerTab from "../Tabs/TriggerTab";
|
||||||
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
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) {
|
public static create(source: ViewModels.Collection, event: MouseEvent) {
|
||||||
const id =
|
const id = source.container.tabsManager.getTabs(ViewModels.CollectionTabKind.Triggers).length + 1;
|
||||||
source.container
|
|
||||||
.openedTabs()
|
|
||||||
.filter((tab: ViewModels.Tab) => tab.tabKind === ViewModels.CollectionTabKind.Triggers).length + 1;
|
|
||||||
const trigger = <DataModels.Trigger>{
|
const trigger = <DataModels.Trigger>{
|
||||||
id: "",
|
id: "",
|
||||||
body: "function trigger(){}",
|
body: "function trigger(){}",
|
||||||
@ -52,7 +48,7 @@ export default class Trigger implements ViewModels.Trigger {
|
|||||||
triggerType: "Pre"
|
triggerType: "Pre"
|
||||||
};
|
};
|
||||||
|
|
||||||
let triggerTab: ViewModels.Tab = new TriggerTab({
|
const triggerTab: TriggerTab = new TriggerTab({
|
||||||
resource: trigger,
|
resource: trigger,
|
||||||
isNew: true,
|
isNew: true,
|
||||||
tabKind: ViewModels.CollectionTabKind.Triggers,
|
tabKind: ViewModels.CollectionTabKind.Triggers,
|
||||||
@ -64,23 +60,24 @@ export default class Trigger implements ViewModels.Trigger {
|
|||||||
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(source.databaseId, source.id())}/trigger`,
|
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(source.databaseId, source.id())}/trigger`,
|
||||||
selfLink: "",
|
selfLink: "",
|
||||||
isActive: ko.observable(false),
|
isActive: ko.observable(false),
|
||||||
onUpdateTabsButtons: source.container.onUpdateTabsButtons,
|
onUpdateTabsButtons: source.container.onUpdateTabsButtons
|
||||||
openedTabs: source.container.openedTabs()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
source.container.openedTabs.push(triggerTab);
|
source.container.tabsManager.activateNewTab(triggerTab);
|
||||||
|
|
||||||
// Activate
|
|
||||||
triggerTab.onTabClick();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public open = () => {
|
public open = () => {
|
||||||
this.select();
|
this.select();
|
||||||
|
|
||||||
let triggerTab: ViewModels.Tab = this.container
|
const triggerTabs: TriggerTab[] = this.container.tabsManager.getTabs(
|
||||||
.openedTabs()
|
ViewModels.CollectionTabKind.Triggers,
|
||||||
.filter(tab => tab.node && tab.node.rid === this.rid)[0];
|
(tab: ViewModels.Tab) => tab.node && tab.node.rid === this.rid
|
||||||
if (!triggerTab) {
|
) as TriggerTab[];
|
||||||
|
let triggerTab: TriggerTab = triggerTabs && triggerTabs[0];
|
||||||
|
|
||||||
|
if (triggerTab) {
|
||||||
|
this.container.tabsManager.activateTab(triggerTab);
|
||||||
|
} else {
|
||||||
const triggerData = <DataModels.Trigger>{
|
const triggerData = <DataModels.Trigger>{
|
||||||
_rid: this.rid,
|
_rid: this.rid,
|
||||||
_self: this.self,
|
_self: this.self,
|
||||||
@ -105,15 +102,11 @@ export default class Trigger implements ViewModels.Trigger {
|
|||||||
)}/triggers/${this.id()}`,
|
)}/triggers/${this.id()}`,
|
||||||
selfLink: "",
|
selfLink: "",
|
||||||
isActive: ko.observable(false),
|
isActive: ko.observable(false),
|
||||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||||
openedTabs: this.container.openedTabs()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.container.openedTabs.push(triggerTab);
|
this.container.tabsManager.activateNewTab(triggerTab);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Activate
|
|
||||||
triggerTab.onTabClick();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public delete() {
|
public delete() {
|
||||||
@ -132,7 +125,9 @@ export default class Trigger implements ViewModels.Trigger {
|
|||||||
|
|
||||||
this.container.documentClientUtility.deleteTrigger(this.collection, triggerData).then(
|
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);
|
this.collection.children.remove(this);
|
||||||
},
|
},
|
||||||
reason => {}
|
reason => {}
|
||||||
|
@ -5,7 +5,6 @@ import * as DataModels from "../../Contracts/DataModels";
|
|||||||
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||||
import UserDefinedFunctionTab from "../Tabs/UserDefinedFunctionTab";
|
import UserDefinedFunctionTab from "../Tabs/UserDefinedFunctionTab";
|
||||||
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import Collection from "./Collection";
|
|
||||||
|
|
||||||
export default class UserDefinedFunction implements ViewModels.UserDefinedFunction {
|
export default class UserDefinedFunction implements ViewModels.UserDefinedFunction {
|
||||||
public nodeKind: string;
|
public nodeKind: string;
|
||||||
@ -28,15 +27,13 @@ export default class UserDefinedFunction implements ViewModels.UserDefinedFuncti
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static create(source: ViewModels.Collection, event: MouseEvent) {
|
public static create(source: ViewModels.Collection, event: MouseEvent) {
|
||||||
const id =
|
const id = source.container.tabsManager.getTabs(ViewModels.CollectionTabKind.UserDefinedFunctions).length + 1;
|
||||||
source.container
|
|
||||||
.openedTabs()
|
|
||||||
.filter((tab: ViewModels.Tab) => tab.tabKind === ViewModels.CollectionTabKind.UserDefinedFunctions).length + 1;
|
|
||||||
const userDefinedFunction = <DataModels.UserDefinedFunction>{
|
const userDefinedFunction = <DataModels.UserDefinedFunction>{
|
||||||
id: "",
|
id: "",
|
||||||
body: "function userDefinedFunction(){}"
|
body: "function userDefinedFunction(){}"
|
||||||
};
|
};
|
||||||
let userDefinedFunctionTab: ViewModels.Tab = new UserDefinedFunctionTab({
|
|
||||||
|
const userDefinedFunctionTab: UserDefinedFunctionTab = new UserDefinedFunctionTab({
|
||||||
resource: userDefinedFunction,
|
resource: userDefinedFunction,
|
||||||
isNew: true,
|
isNew: true,
|
||||||
tabKind: ViewModels.CollectionTabKind.UserDefinedFunctions,
|
tabKind: ViewModels.CollectionTabKind.UserDefinedFunctions,
|
||||||
@ -48,22 +45,24 @@ export default class UserDefinedFunction implements ViewModels.UserDefinedFuncti
|
|||||||
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(source.databaseId, source.id())}/udf`,
|
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(source.databaseId, source.id())}/udf`,
|
||||||
selfLink: "",
|
selfLink: "",
|
||||||
isActive: ko.observable(false),
|
isActive: ko.observable(false),
|
||||||
onUpdateTabsButtons: source.container.onUpdateTabsButtons,
|
onUpdateTabsButtons: source.container.onUpdateTabsButtons
|
||||||
openedTabs: source.container.openedTabs()
|
|
||||||
});
|
});
|
||||||
source.container.openedTabs.push(userDefinedFunctionTab);
|
|
||||||
|
|
||||||
// Activate
|
source.container.tabsManager.activateNewTab(userDefinedFunctionTab);
|
||||||
userDefinedFunctionTab.onTabClick();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public open = () => {
|
public open = () => {
|
||||||
this.select();
|
this.select();
|
||||||
|
|
||||||
let userDefinedFunctionTab: ViewModels.Tab = this.container
|
const userDefinedFunctionTabs: UserDefinedFunctionTab[] = this.container.tabsManager.getTabs(
|
||||||
.openedTabs()
|
ViewModels.CollectionTabKind.UserDefinedFunctions,
|
||||||
.filter(tab => tab.node && tab.node.rid === this.rid)[0];
|
(tab: ViewModels.Tab) => tab.collection && tab.collection.rid === this.rid
|
||||||
if (!userDefinedFunctionTab) {
|
) as UserDefinedFunctionTab[];
|
||||||
|
let userDefinedFunctionTab: UserDefinedFunctionTab = userDefinedFunctionTabs && userDefinedFunctionTabs[0];
|
||||||
|
|
||||||
|
if (userDefinedFunctionTab) {
|
||||||
|
this.container.tabsManager.activateTab(userDefinedFunctionTab);
|
||||||
|
} else {
|
||||||
const userDefinedFunctionData = <DataModels.UserDefinedFunction>{
|
const userDefinedFunctionData = <DataModels.UserDefinedFunction>{
|
||||||
_rid: this.rid,
|
_rid: this.rid,
|
||||||
_self: this.self,
|
_self: this.self,
|
||||||
@ -86,14 +85,11 @@ export default class UserDefinedFunction implements ViewModels.UserDefinedFuncti
|
|||||||
)}/udfs/${this.id()}`,
|
)}/udfs/${this.id()}`,
|
||||||
selfLink: "",
|
selfLink: "",
|
||||||
isActive: ko.observable(false),
|
isActive: ko.observable(false),
|
||||||
onUpdateTabsButtons: this.container.onUpdateTabsButtons,
|
onUpdateTabsButtons: this.container.onUpdateTabsButtons
|
||||||
openedTabs: this.container.openedTabs()
|
|
||||||
});
|
});
|
||||||
this.container.openedTabs.push(userDefinedFunctionTab);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Activate
|
this.container.tabsManager.activateNewTab(userDefinedFunctionTab);
|
||||||
userDefinedFunctionTab.onTabClick();
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public select() {
|
public select() {
|
||||||
@ -119,7 +115,9 @@ export default class UserDefinedFunction implements ViewModels.UserDefinedFuncti
|
|||||||
};
|
};
|
||||||
this.container.documentClientUtility.deleteUserDefinedFunction(this.collection, userDefinedFunctionData).then(
|
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);
|
this.collection.children.remove(this);
|
||||||
},
|
},
|
||||||
reason => {}
|
reason => {}
|
||||||
|
@ -390,17 +390,15 @@ export class TabRouteHandler {
|
|||||||
isNewScriptTab?: boolean
|
isNewScriptTab?: boolean
|
||||||
): ViewModels.Tab {
|
): ViewModels.Tab {
|
||||||
const explorer: ViewModels.Explorer = (<any>window).dataExplorer;
|
const explorer: ViewModels.Explorer = (<any>window).dataExplorer;
|
||||||
|
const matchingTabs: ViewModels.Tab[] = explorer.tabsManager.getTabs(
|
||||||
return _.find(explorer.openedTabs(), (tab: ViewModels.Tab) => {
|
tabKind,
|
||||||
const isMatchingTabKind =
|
(tab: ViewModels.Tab) =>
|
||||||
tab.collection.databaseId === databaseId && tab.collection.id() === collectionId && tab.tabKind === tabKind;
|
tab.collection &&
|
||||||
|
tab.collection.databaseId === databaseId &&
|
||||||
if (isNewScriptTab) {
|
tab.collection.id() === collectionId &&
|
||||||
return isMatchingTabKind && (tab as ViewModels.ScriptTab).isNew();
|
(!isNewScriptTab || (tab as ViewModels.ScriptTab).isNew())
|
||||||
}
|
);
|
||||||
|
return matchingTabs && matchingTabs[0];
|
||||||
return isMatchingTabKind;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _findMatchingCollectionForResource(databaseId: string, collectionId: string): ViewModels.Collection {
|
private _findMatchingCollectionForResource(databaseId: string, collectionId: string): ViewModels.Collection {
|
||||||
|
@ -187,171 +187,13 @@
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
class="connectExplorerContainer"
|
class="connectExplorerContainer"
|
||||||
data-bind="visible: !isRefreshingExplorer() && (openedTabs == null || openedTabs().length === 0)"
|
data-bind="visible: !isRefreshingExplorer() && tabsManager.openedTabs().length === 0"
|
||||||
>
|
>
|
||||||
<form class="connectExplorerFormContainer">
|
<form class="connectExplorerFormContainer">
|
||||||
<div class="connectExplorer" data-bind="react: splashScreenAdapter"></div>
|
<div class="connectExplorer" data-bind="react: splashScreenAdapter"></div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
<tabs-manager class="tabsContainer" params="{data: tabsManager}"></tabs-manager>
|
||||||
<!-- 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 -->
|
|
||||||
</div>
|
</div>
|
||||||
<!-- Collections Tree and Tabs - End -->
|
<!-- Collections Tree and Tabs - End -->
|
||||||
<div
|
<div
|
||||||
|
Loading…
Reference in New Issue
Block a user