mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-13 17:22:15 -05:00
Support left nav bar and inner tabs (#751)
* Add left nav bar and inner tab contribution * place holder for dashboard left nav bar * formatting * refactor widget helper * improving the panel look and feel * removed extra files that added by accident
This commit is contained in:
@@ -16,6 +16,8 @@
|
||||
</dashboard-webview-tab>
|
||||
<dashboard-widget-tab *ngIf="getContentType(tab) === 'widgets-tab'" [tab]="tab">
|
||||
</dashboard-widget-tab>
|
||||
<dashboard-left-nav-bar *ngIf="getContentType(tab) === 'left-nav-bar'" [tab]="tab">
|
||||
</dashboard-left-nav-bar>
|
||||
<dashboard-grid-tab *ngIf="getContentType(tab) === 'grid-tab'" [tab]="tab">
|
||||
</dashboard-grid-tab>
|
||||
</tab>
|
||||
|
||||
@@ -10,22 +10,20 @@ import { Component, Inject, forwardRef, ViewChild, ElementRef, ViewChildren, Que
|
||||
|
||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||
import { WidgetConfig, TabConfig, PinConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||
import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo';
|
||||
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
|
||||
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/common/dashboardWidgetWrapper.component';
|
||||
import { IPropertiesConfig } from 'sql/parts/dashboard/pages/serverDashboardPage.contribution';
|
||||
import { PanelComponent } from 'sql/base/browser/ui/panel/panel.component';
|
||||
import { subscriptionToDisposable } from 'sql/base/common/lifecycle';
|
||||
import { IDashboardRegistry, Extensions as DashboardExtensions, IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
|
||||
import { PinUnpinTabAction, AddFeatureTabAction } from './actions';
|
||||
import { TabComponent } from 'sql/base/browser/ui/panel/tab.component';
|
||||
import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService';
|
||||
import { AngularEventType, IAngularEvent } from 'sql/services/angularEventing/angularEventingService';
|
||||
import { AngularEventType } from 'sql/services/angularEventing/angularEventingService';
|
||||
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
|
||||
import { error } from 'sql/base/common/log';
|
||||
import { WIDGETS_TABS } from 'sql/parts/dashboard/tabs/dashboardWidgetTab.contribution';
|
||||
import { GRID_TABS } from 'sql/parts/dashboard/tabs/dashboardGridTab.contribution';
|
||||
import { WEBVIEW_TABS } from 'sql/parts/dashboard/tabs/dashboardWebviewTab.contribution';
|
||||
import * as widgetHelper from 'sql/parts/dashboard/common/dashboardWidgetHelper';
|
||||
import { WIDGETS_TAB } from 'sql/parts/dashboard/tabs/dashboardWidgetTab.contribution';
|
||||
import { GRID_TAB } from 'sql/parts/dashboard/tabs/dashboardGridTab.contribution';
|
||||
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import * as types from 'vs/base/common/types';
|
||||
@@ -38,7 +36,6 @@ import { addDisposableListener, getContentHeight, EventType } from 'vs/base/brow
|
||||
import { IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
||||
import * as themeColors from 'vs/workbench/common/theme';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
@@ -46,12 +43,6 @@ import { ConfigurationTarget } from 'vs/platform/configuration/common/configurat
|
||||
|
||||
const dashboardRegistry = Registry.as<IDashboardRegistry>(DashboardExtensions.DashboardContributions);
|
||||
|
||||
/**
|
||||
* @returns whether the provided parameter is a JavaScript Array and each element in the array is a number.
|
||||
*/
|
||||
function isNumberArray(value: any): value is number[] {
|
||||
return types.isArray(value) && (<any[]>value).every(elem => types.isNumber(elem));
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'dashboard-page',
|
||||
@@ -87,17 +78,17 @@ export abstract class DashboardPage extends Disposable implements OnDestroy {
|
||||
private readonly homeTabTitle: string = nls.localize('home', 'Home');
|
||||
|
||||
// a set of config modifiers
|
||||
private readonly _configModifiers: Array<(item: Array<WidgetConfig>) => Array<WidgetConfig>> = [
|
||||
this.removeEmpty,
|
||||
this.initExtensionConfigs,
|
||||
this.addProvider,
|
||||
this.addEdition,
|
||||
this.addContext,
|
||||
this.filterConfigs
|
||||
private readonly _configModifiers: Array<(item: Array<WidgetConfig>, dashboardServer: DashboardServiceInterface, context: string) => Array<WidgetConfig>> = [
|
||||
widgetHelper.removeEmpty,
|
||||
widgetHelper.initExtensionConfigs,
|
||||
widgetHelper.addProvider,
|
||||
widgetHelper.addEdition,
|
||||
widgetHelper.addContext,
|
||||
widgetHelper.filterConfigs
|
||||
];
|
||||
|
||||
private readonly _gridModifiers: Array<(item: Array<WidgetConfig>) => Array<WidgetConfig>> = [
|
||||
this.validateGridConfig
|
||||
private readonly _gridModifiers: Array<(item: Array<WidgetConfig>, originalConfig: Array<WidgetConfig>) => Array<WidgetConfig>> = [
|
||||
widgetHelper.validateGridConfig
|
||||
];
|
||||
|
||||
constructor(
|
||||
@@ -118,11 +109,11 @@ export abstract class DashboardPage extends Disposable implements OnDestroy {
|
||||
this._originalConfig = objects.deepClone(tempWidgets);
|
||||
let properties = this.getProperties();
|
||||
this._configModifiers.forEach((cb) => {
|
||||
tempWidgets = cb.apply(this, [tempWidgets]);
|
||||
properties = properties ? cb.apply(this, [properties]) : undefined;
|
||||
tempWidgets = cb.apply(this, [tempWidgets, this.dashboardService, this.context]);
|
||||
properties = properties ? cb.apply(this, [properties, this.dashboardService, this.context]) : undefined;
|
||||
});
|
||||
this._gridModifiers.forEach(cb => {
|
||||
tempWidgets = cb.apply(this, [tempWidgets]);
|
||||
tempWidgets = cb.apply(this, [tempWidgets, this._originalConfig]);
|
||||
});
|
||||
this.propertiesWidget = properties ? properties[0] : undefined;
|
||||
|
||||
@@ -198,7 +189,7 @@ export abstract class DashboardPage extends Disposable implements OnDestroy {
|
||||
this.addNewTab(homeTab);
|
||||
this._panel.selectTab(homeTab.id);
|
||||
|
||||
let allTabs = this.filterConfigs(dashboardRegistry.tabs);
|
||||
let allTabs = widgetHelper.filterConfigs(dashboardRegistry.tabs, this.dashboardService);
|
||||
|
||||
// Load always show tabs
|
||||
let alwaysShowTabs = allTabs.filter(tab => tab.alwaysShow);
|
||||
@@ -252,19 +243,19 @@ export abstract class DashboardPage extends Disposable implements OnDestroy {
|
||||
let selectedTabs = dashboardTabs.map(v => {
|
||||
|
||||
if (Object.keys(v.content).length !== 1) {
|
||||
error('Exactly 1 widget must be defined per space');
|
||||
error('Exactly 1 content must be defined per space');
|
||||
}
|
||||
|
||||
let key = Object.keys(v.content)[0];
|
||||
if (key === WIDGETS_TABS || key === GRID_TABS) {
|
||||
if (key === WIDGETS_TAB || key === GRID_TAB) {
|
||||
let configs = <WidgetConfig[]>Object.values(v.content)[0];
|
||||
this._configModifiers.forEach(cb => {
|
||||
configs = cb.apply(this, [configs]);
|
||||
configs = cb.apply(this, [configs, this.dashboardService, this.context]);
|
||||
});
|
||||
this._gridModifiers.forEach(cb => {
|
||||
configs = cb.apply(this, [configs]);
|
||||
configs = cb.apply(this, [configs, this._originalConfig]);
|
||||
});
|
||||
if (key === WIDGETS_TABS) {
|
||||
if (key === WIDGETS_TAB) {
|
||||
return { id: v.id, title: v.title, content: { 'widgets-tab': configs }, alwaysShow: v.alwaysShow };
|
||||
|
||||
} else {
|
||||
@@ -339,158 +330,6 @@ export abstract class DashboardPage extends Disposable implements OnDestroy {
|
||||
protected abstract propertiesWidget: WidgetConfig;
|
||||
protected abstract get context(): string;
|
||||
|
||||
/**
|
||||
* Returns a filtered version of the widgets passed based on edition and provider
|
||||
* @param config widgets to filter
|
||||
*/
|
||||
private filterConfigs<T extends { provider?: string | string[], edition?: number | number[] }>(config: T[]): Array<T> {
|
||||
let connectionInfo: ConnectionManagementInfo = this.dashboardService.connectionManagementService.connectionInfo;
|
||||
let edition = connectionInfo.serverInfo.engineEditionId;
|
||||
let provider = connectionInfo.providerId;
|
||||
|
||||
// filter by provider
|
||||
return config.filter((item) => {
|
||||
if (item.provider) {
|
||||
return this.stringCompare(item.provider, provider);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}).filter((item) => {
|
||||
if (item.edition) {
|
||||
if (edition) {
|
||||
return this.stringCompare(isNumberArray(item.edition) ? item.edition.map(item => item.toString()) : item.edition.toString(), edition.toString());
|
||||
} else {
|
||||
this.dashboardService.messageService.show(Severity.Warning, nls.localize('providerMissingEdition', 'Widget filters based on edition, but the provider does not have an edition'));
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a compare against the val passed in and the compare string
|
||||
* @param val string or array of strings to compare the compare value to; if array, it will compare each val in the array
|
||||
* @param compare value to compare to
|
||||
*/
|
||||
private stringCompare(val: string | Array<string>, compare: string): boolean {
|
||||
if (types.isUndefinedOrNull(val)) {
|
||||
return true;
|
||||
} else if (types.isString(val)) {
|
||||
return val === compare;
|
||||
} else if (types.isStringArray(val)) {
|
||||
return val.some(item => item === compare);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add provider to the passed widgets and returns the new widgets
|
||||
* @param widgets Array of widgets to add provider onto
|
||||
*/
|
||||
protected addProvider(config: WidgetConfig[]): Array<WidgetConfig> {
|
||||
let provider = this.dashboardService.connectionManagementService.connectionInfo.providerId;
|
||||
return config.map((item) => {
|
||||
if (item.provider === undefined) {
|
||||
item.provider = provider;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the edition to the passed widgets and returns the new widgets
|
||||
* @param widgets Array of widgets to add edition onto
|
||||
*/
|
||||
protected addEdition(config: WidgetConfig[]): Array<WidgetConfig> {
|
||||
let connectionInfo: ConnectionManagementInfo = this.dashboardService.connectionManagementService.connectionInfo;
|
||||
let edition = connectionInfo.serverInfo.engineEditionId;
|
||||
return config.map((item) => {
|
||||
if (item.edition === undefined) {
|
||||
item.edition = edition;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the context to the passed widgets and returns the new widgets
|
||||
* @param widgets Array of widgets to add context to
|
||||
*/
|
||||
protected addContext(config: WidgetConfig[]): Array<WidgetConfig> {
|
||||
let context = this.context;
|
||||
return config.map((item) => {
|
||||
if (item.context === undefined) {
|
||||
item.context = context;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates configs to make sure nothing will error out and returns the modified widgets
|
||||
* @param config Array of widgets to validate
|
||||
*/
|
||||
protected removeEmpty(config: WidgetConfig[]): Array<WidgetConfig> {
|
||||
return config.filter(widget => {
|
||||
return !types.isUndefinedOrNull(widget);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates configs to make sure nothing will error out and returns the modified widgets
|
||||
* @param config Array of widgets to validate
|
||||
*/
|
||||
protected validateGridConfig(config: WidgetConfig[]): Array<WidgetConfig> {
|
||||
return config.map((widget, index) => {
|
||||
if (widget.gridItemConfig === undefined) {
|
||||
widget.gridItemConfig = {};
|
||||
}
|
||||
const id = generateUuid();
|
||||
widget.gridItemConfig.payload = { id };
|
||||
widget.id = id;
|
||||
this._originalConfig[index].id = id;
|
||||
return widget;
|
||||
});
|
||||
}
|
||||
|
||||
protected initExtensionConfigs(configurations: WidgetConfig[]): Array<WidgetConfig> {
|
||||
let widgetRegistry = <IInsightRegistry>Registry.as(Extensions.InsightContribution);
|
||||
return configurations.map((config) => {
|
||||
if (config.widget && Object.keys(config.widget).length === 1) {
|
||||
let key = Object.keys(config.widget)[0];
|
||||
let insightConfig = widgetRegistry.getRegisteredExtensionInsights(key);
|
||||
if (insightConfig !== undefined) {
|
||||
// Setup the default properties for this extension if needed
|
||||
if (!config.provider && insightConfig.provider) {
|
||||
config.provider = insightConfig.provider;
|
||||
}
|
||||
if (!config.name && insightConfig.name) {
|
||||
config.name = insightConfig.name;
|
||||
}
|
||||
if (!config.edition && insightConfig.edition) {
|
||||
config.edition = insightConfig.edition;
|
||||
}
|
||||
if (!config.gridItemConfig && insightConfig.gridItemConfig) {
|
||||
config.gridItemConfig = {
|
||||
sizex: insightConfig.gridItemConfig.x,
|
||||
sizey: insightConfig.gridItemConfig.y
|
||||
};
|
||||
}
|
||||
if (config.gridItemConfig && !config.gridItemConfig.sizex && insightConfig.gridItemConfig && insightConfig.gridItemConfig.x) {
|
||||
config.gridItemConfig.sizex = insightConfig.gridItemConfig.x;
|
||||
}
|
||||
if (config.gridItemConfig && !config.gridItemConfig.sizey && insightConfig.gridItemConfig && insightConfig.gridItemConfig.y) {
|
||||
config.gridItemConfig.sizey = insightConfig.gridItemConfig.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
return config;
|
||||
});
|
||||
}
|
||||
|
||||
private getProperties(): Array<WidgetConfig> {
|
||||
let properties = this.dashboardService.getSettings<IPropertiesConfig[]>([this.context, 'properties'].join('.'));
|
||||
this._propertiesConfigLocation = 'default';
|
||||
|
||||
@@ -13,6 +13,7 @@ panel.dashboard-panel > .tabbedPanel > .title > .tabList .tab-header .tab > .tab
|
||||
border-bottom: 0px solid;
|
||||
}
|
||||
|
||||
panel.dashboard-panel > .tabbedPanel > .title > .title-actions,
|
||||
panel.dashboard-panel > .tabbedPanel > .title > .tabList .tab-header {
|
||||
box-sizing: border-box;
|
||||
border: 1px solid transparent;
|
||||
|
||||
@@ -24,6 +24,14 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
panel.dashboard-panel > .tabbedPanel > .title > .tabList .tab-header.active {
|
||||
background-color: ${tabActiveBackground};
|
||||
}
|
||||
|
||||
panel.dashboard-panel > .tabbedPanel.horizontal > .title > .tabList .tab-header.active {
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
|
||||
panel.dashboard-panel > .tabbedPanel.vertical > .title > .tabList .tab-header.active {
|
||||
border-right-color: transparent;
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
@@ -65,13 +73,18 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
const tabBoarder = theme.getColor(TAB_BORDER);
|
||||
if (tabBoarder) {
|
||||
collector.addRule(`
|
||||
panel.dashboard-panel > .tabbedPanel.horizontal > .title > .tabList .tab-header {
|
||||
panel.dashboard-panel > .tabbedPanel > .title > .tabList .tab-header {
|
||||
border-right-color: ${tabBoarder};
|
||||
border-bottom-color: ${tabBoarder};
|
||||
}
|
||||
|
||||
panel.dashboard-panel > .tabbedPanel.vertical > .title > .tabList .tab-header {
|
||||
panel.dashboard-panel > .tabbedPanel.horizontal > .title > .title-actions {
|
||||
border-bottom-color: ${tabBoarder};
|
||||
}
|
||||
|
||||
panel.dashboard-panel > .tabbedPanel.vertical > .title > .title-actions {
|
||||
border-right-color: ${tabBoarder};
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
|
||||
175
src/sql/parts/dashboard/common/dashboardWidgetHelper.ts
Normal file
175
src/sql/parts/dashboard/common/dashboardWidgetHelper.ts
Normal file
@@ -0,0 +1,175 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Severity } from 'vs/platform/message/common/message';
|
||||
import * as nls from 'vs/nls';
|
||||
|
||||
import { WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
|
||||
import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo';
|
||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||
|
||||
|
||||
/**
|
||||
* @returns whether the provided parameter is a JavaScript Array and each element in the array is a number.
|
||||
*/
|
||||
function isNumberArray(value: any): value is number[] {
|
||||
return types.isArray(value) && (<any[]>value).every(elem => types.isNumber(elem));
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a compare against the val passed in and the compare string
|
||||
* @param val string or array of strings to compare the compare value to; if array, it will compare each val in the array
|
||||
* @param compare value to compare to
|
||||
*/
|
||||
function stringOrStringArrayCompare(val: string | Array<string>, compare: string): boolean {
|
||||
if (types.isUndefinedOrNull(val)) {
|
||||
return true;
|
||||
} else if (types.isString(val)) {
|
||||
return val === compare;
|
||||
} else if (types.isStringArray(val)) {
|
||||
return val.some(item => item === compare);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates configs to make sure nothing will error out and returns the modified widgets
|
||||
* @param config Array of widgets to validate
|
||||
*/
|
||||
export function removeEmpty(config: WidgetConfig[]): Array<WidgetConfig> {
|
||||
return config.filter(widget => {
|
||||
return !types.isUndefinedOrNull(widget);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates configs to make sure nothing will error out and returns the modified widgets
|
||||
* @param config Array of widgets to validate
|
||||
*/
|
||||
export function validateGridConfig(config: WidgetConfig[], originalConfig: WidgetConfig[]): Array<WidgetConfig> {
|
||||
return config.map((widget, index) => {
|
||||
if (widget.gridItemConfig === undefined) {
|
||||
widget.gridItemConfig = {};
|
||||
}
|
||||
const id = generateUuid();
|
||||
widget.gridItemConfig.payload = { id };
|
||||
widget.id = id;
|
||||
if (originalConfig && originalConfig[index]) {
|
||||
originalConfig[index].id = id;
|
||||
}
|
||||
return widget;
|
||||
});
|
||||
}
|
||||
|
||||
export function initExtensionConfigs(configurations: WidgetConfig[]): Array<WidgetConfig> {
|
||||
let widgetRegistry = <IInsightRegistry>Registry.as(Extensions.InsightContribution);
|
||||
return configurations.map((config) => {
|
||||
if (config.widget && Object.keys(config.widget).length === 1) {
|
||||
let key = Object.keys(config.widget)[0];
|
||||
let insightConfig = widgetRegistry.getRegisteredExtensionInsights(key);
|
||||
if (insightConfig !== undefined) {
|
||||
// Setup the default properties for this extension if needed
|
||||
if (!config.provider && insightConfig.provider) {
|
||||
config.provider = insightConfig.provider;
|
||||
}
|
||||
if (!config.name && insightConfig.name) {
|
||||
config.name = insightConfig.name;
|
||||
}
|
||||
if (!config.edition && insightConfig.edition) {
|
||||
config.edition = insightConfig.edition;
|
||||
}
|
||||
if (!config.gridItemConfig && insightConfig.gridItemConfig) {
|
||||
config.gridItemConfig = {
|
||||
sizex: insightConfig.gridItemConfig.x,
|
||||
sizey: insightConfig.gridItemConfig.y
|
||||
};
|
||||
}
|
||||
if (config.gridItemConfig && !config.gridItemConfig.sizex && insightConfig.gridItemConfig && insightConfig.gridItemConfig.x) {
|
||||
config.gridItemConfig.sizex = insightConfig.gridItemConfig.x;
|
||||
}
|
||||
if (config.gridItemConfig && !config.gridItemConfig.sizey && insightConfig.gridItemConfig && insightConfig.gridItemConfig.y) {
|
||||
config.gridItemConfig.sizey = insightConfig.gridItemConfig.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
return config;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add provider to the passed widgets and returns the new widgets
|
||||
* @param widgets Array of widgets to add provider onto
|
||||
*/
|
||||
export function addProvider(config: WidgetConfig[], dashboardService: DashboardServiceInterface): Array<WidgetConfig> {
|
||||
let provider = dashboardService.connectionManagementService.connectionInfo.providerId;
|
||||
return config.map((item) => {
|
||||
if (item.provider === undefined) {
|
||||
item.provider = provider;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the edition to the passed widgets and returns the new widgets
|
||||
* @param widgets Array of widgets to add edition onto
|
||||
*/
|
||||
export function addEdition(config: WidgetConfig[], dashboardService: DashboardServiceInterface): Array<WidgetConfig> {
|
||||
let connectionInfo: ConnectionManagementInfo = dashboardService.connectionManagementService.connectionInfo;
|
||||
let edition = connectionInfo.serverInfo.engineEditionId;
|
||||
return config.map((item) => {
|
||||
if (item.edition === undefined) {
|
||||
item.edition = edition;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the context to the passed widgets and returns the new widgets
|
||||
* @param widgets Array of widgets to add context to
|
||||
*/
|
||||
export function addContext(config: WidgetConfig[], dashboardServer: DashboardServiceInterface, context: string): Array<WidgetConfig> {
|
||||
return config.map((item) => {
|
||||
if (item.context === undefined) {
|
||||
item.context = context;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a filtered version of the widgets passed based on edition and provider
|
||||
* @param config widgets to filter
|
||||
*/
|
||||
export function filterConfigs<T extends { provider?: string | string[], edition?: number | number[] }>(config: T[], dashboardService: DashboardServiceInterface): Array<T> {
|
||||
let connectionInfo: ConnectionManagementInfo = dashboardService.connectionManagementService.connectionInfo;
|
||||
let edition = connectionInfo.serverInfo.engineEditionId;
|
||||
let provider = connectionInfo.providerId;
|
||||
|
||||
// filter by provider
|
||||
return config.filter((item) => {
|
||||
if (item.provider) {
|
||||
return stringOrStringArrayCompare(item.provider, provider);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}).filter((item) => {
|
||||
if (item.edition) {
|
||||
if (edition) {
|
||||
return stringOrStringArrayCompare(isNumberArray(item.edition) ? item.edition.map(item => item.toString()) : item.edition.toString(), edition.toString());
|
||||
} else {
|
||||
dashboardService.messageService.show(Severity.Warning, nls.localize('providerMissingEdition', 'Widget filters based on edition, but the provider does not have an edition'));
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -30,11 +30,12 @@ import { DashboardWidgetWrapper } from 'sql/parts/dashboard/common/dashboardWidg
|
||||
import { DashboardWidgetTab } from 'sql/parts/dashboard/tabs/dashboardWidgetTab.component';
|
||||
import { DashboardGridTab } from 'sql/parts/dashboard/tabs/dashboardGridTab.component';
|
||||
import { DashboardWebviewTab } from 'sql/parts/dashboard/tabs/dashboardWebviewTab.component';
|
||||
import { DashboardLeftNavBar } from 'sql/parts/dashboard/tabs/dashboardLeftNavBar.component';
|
||||
import { WidgetContent } from 'sql/parts/dashboard/contents/widgetContent.component';
|
||||
import { WebviewContent } from 'sql/parts/dashboard/contents/webviewContent.component';
|
||||
import { BreadcrumbComponent } from 'sql/base/browser/ui/breadcrumb/breadcrumb.component';
|
||||
import { IBreadcrumbService } from 'sql/base/browser/ui/breadcrumb/interfaces';
|
||||
let baseComponents = [DashboardComponent, DashboardWidgetWrapper, DashboardWebviewTab, DashboardWidgetTab, DashboardGridTab, WidgetContent, WebviewContent, ComponentHostDirective, BreadcrumbComponent];
|
||||
let baseComponents = [DashboardComponent, DashboardWidgetWrapper, DashboardWebviewTab, DashboardWidgetTab, DashboardGridTab, DashboardLeftNavBar, WidgetContent, WebviewContent, ComponentHostDirective, BreadcrumbComponent];
|
||||
|
||||
/* Panel */
|
||||
import { PanelModule } from 'sql/base/browser/ui/panel/panel.module';
|
||||
|
||||
@@ -8,7 +8,7 @@ import * as nls from 'vs/nls';
|
||||
import { generateDashboardGridLayoutSchema } from 'sql/parts/dashboard/pages/dashboardPageContribution';
|
||||
import { registerTabContent } from 'sql/platform/dashboard/common/dashboardRegistry';
|
||||
|
||||
export const GRID_TABS = 'grid-tab';
|
||||
export const GRID_TAB = 'grid-tab';
|
||||
|
||||
let gridContentsSchema: IJSONSchema = {
|
||||
type: 'array',
|
||||
@@ -16,4 +16,4 @@ let gridContentsSchema: IJSONSchema = {
|
||||
items: generateDashboardGridLayoutSchema(undefined, true)
|
||||
};
|
||||
|
||||
registerTabContent(GRID_TABS, gridContentsSchema);
|
||||
registerTabContent(GRID_TAB, gridContentsSchema);
|
||||
|
||||
111
src/sql/parts/dashboard/tabs/dashboardInnerTab.contribution.ts
Normal file
111
src/sql/parts/dashboard/tabs/dashboardInnerTab.contribution.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { IExtensionPointUser, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import { localize } from 'vs/nls';
|
||||
import { join } from 'path';
|
||||
import { createCSSRule } from 'vs/base/browser/dom';
|
||||
import URI from 'vs/base/common/uri';
|
||||
|
||||
import { registerInnerTab, generateInnerTabContentSchemaProperties } from 'sql/platform/dashboard/common/innerTabRegistry';
|
||||
|
||||
export type IUserFriendlyIcon = string | { light: string; dark: string; };
|
||||
|
||||
export interface IDashboardInnerTabContrib {
|
||||
id: string;
|
||||
title: string;
|
||||
icon?: IUserFriendlyIcon;
|
||||
content: object;
|
||||
}
|
||||
|
||||
const innerTabSchema: IJSONSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: localize('sqlops.extension.contributes.dashboard.innertab.id', "Unique identifier for this inner tab. Will be passed to the extension for any requests.")
|
||||
},
|
||||
icon: {
|
||||
description: localize('sqlops.extension.contributes.dashboard.innertab.icon', '(Optional) Icon which is used to represent this inner tab in the UI. Either a file path or a themable configuration'),
|
||||
anyOf: [{
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
light: {
|
||||
description: localize('carbon.extension.contributes.account.icon.light', 'Icon path when a light theme is used'),
|
||||
type: 'string'
|
||||
},
|
||||
dark: {
|
||||
description: localize('carbon.extension.contributes.account.icon.dark', 'Icon path when a dark theme is used'),
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
}]
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
description: localize('sqlops.extension.contributes.dashboard.innertab.title', "Title of the inner tab to show the user.")
|
||||
},
|
||||
content: {
|
||||
description: localize('sqlops.extension.contributes.dashboard.innertab.content', "The content that will be displayed in this inner tab."),
|
||||
type: 'object',
|
||||
properties: generateInnerTabContentSchemaProperties()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const innerTabContributionSchema: IJSONSchema = {
|
||||
description: localize('sqlops.extension.contributes.innertabs', "Contributes a single or multiple inner tabs for users to add to their dashboard."),
|
||||
oneOf: [
|
||||
innerTabSchema,
|
||||
{
|
||||
type: 'array',
|
||||
items: innerTabSchema
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
ExtensionsRegistry.registerExtensionPoint<IDashboardInnerTabContrib | IDashboardInnerTabContrib[]>('dashboard.innertabs', [], innerTabContributionSchema).setHandler(extensions => {
|
||||
|
||||
function handleCommand(innerTab: IDashboardInnerTabContrib, extension: IExtensionPointUser<any>) {
|
||||
let { title, id, content, icon } = innerTab;
|
||||
if (!title) {
|
||||
extension.collector.error('No title specified for extension.');
|
||||
return;
|
||||
}
|
||||
if (!content) {
|
||||
extension.collector.warn('No content specified to show.');
|
||||
}
|
||||
|
||||
let iconClass: string;
|
||||
if (icon) {
|
||||
iconClass = id;
|
||||
if (typeof icon === 'string') {
|
||||
const path = join(extension.description.extensionFolderPath, icon);
|
||||
createCSSRule(`.icon.${iconClass}`, `background-image: url("${URI.file(path).toString()}")`);
|
||||
} else {
|
||||
const light = join(extension.description.extensionFolderPath, icon.light);
|
||||
const dark = join(extension.description.extensionFolderPath, icon.dark);
|
||||
createCSSRule(`.icon.${iconClass}`, `background-image: url("${URI.file(light).toString()}")`);
|
||||
createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: url("${URI.file(dark).toString()}")`);
|
||||
}
|
||||
}
|
||||
|
||||
registerInnerTab({ title, id, content, hasIcon: !!icon });
|
||||
}
|
||||
|
||||
for (let extension of extensions) {
|
||||
const { value } = extension;
|
||||
if (Array.isArray<IDashboardInnerTabContrib>(value)) {
|
||||
for (let command of value) {
|
||||
handleCommand(command, extension);
|
||||
}
|
||||
} else {
|
||||
handleCommand(value, extension);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
<!--
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
<panel [options]="panelOpt" style="flex: 1 1 auto;" class="dashboard-panel" (onTabChange)="handleTabChange($event)">
|
||||
<tab *ngFor="let tab of tabs" [title]="tab.title" class="fullsize" [identifier]="tab.id" [canClose]="tab.canClose" [actions]="tab.actions">
|
||||
<dashboard-webview-tab *ngIf="getContentType(tab) === 'webview-tab'" [tab]="tab">
|
||||
</dashboard-webview-tab>
|
||||
<dashboard-widget-tab *ngIf="getContentType(tab) === 'widgets-tab'" [tab]="tab">
|
||||
</dashboard-widget-tab>
|
||||
</tab>
|
||||
</panel>
|
||||
|
||||
176
src/sql/parts/dashboard/tabs/dashboardLeftNavBar.component.ts
Normal file
176
src/sql/parts/dashboard/tabs/dashboardLeftNavBar.component.ts
Normal file
@@ -0,0 +1,176 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./dashboardLeftNavBar';
|
||||
|
||||
import { Component, Inject, Input, forwardRef, ViewChild, ElementRef, ViewChildren, QueryList, OnDestroy, ChangeDetectorRef, EventEmitter, OnChanges, AfterContentInit } from '@angular/core';
|
||||
|
||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||
import { WidgetConfig, TabConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||
import { PanelComponent, IPanelOptions, NavigationBarLayout } from 'sql/base/browser/ui/panel/panel.component';
|
||||
import { IDashboardInnerTabRegistry, Extensions as InnerTabExtensions, IDashboardInnerTab } from 'sql/platform/dashboard/common/innerTabRegistry';
|
||||
import { TabComponent } from 'sql/base/browser/ui/panel/tab.component';
|
||||
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
|
||||
import { error } from 'sql/base/common/log';
|
||||
import { WIDGETS_TAB } from 'sql/parts/dashboard/tabs/dashboardWidgetTab.contribution';
|
||||
import * as widgetHelper from 'sql/parts/dashboard/common/dashboardWidgetHelper';
|
||||
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
|
||||
const innerTabRegistry = Registry.as<IDashboardInnerTabRegistry>(InnerTabExtensions.InnerTabContributions);
|
||||
|
||||
@Component({
|
||||
selector: 'dashboard-left-nav-bar',
|
||||
providers: [{ provide: DashboardTab, useExisting: forwardRef(() => DashboardLeftNavBar) }],
|
||||
templateUrl: decodeURI(require.toUrl('sql/parts/dashboard/tabs/dashboardLeftNavBar.component.html'))
|
||||
})
|
||||
export class DashboardLeftNavBar extends DashboardTab implements OnDestroy, OnChanges, AfterContentInit {
|
||||
@Input() private tab: TabConfig;
|
||||
protected tabs: Array<TabConfig> = [];
|
||||
private _onResize = new Emitter<void>();
|
||||
public readonly onResize: Event<void> = this._onResize.event;
|
||||
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
private readonly panelOpt: IPanelOptions = {
|
||||
layout: NavigationBarLayout.vertical
|
||||
};
|
||||
|
||||
// a set of config modifiers
|
||||
private readonly _configModifiers: Array<(item: Array<WidgetConfig>, dashboardServer: DashboardServiceInterface, context: string) => Array<WidgetConfig>> = [
|
||||
widgetHelper.removeEmpty,
|
||||
widgetHelper.initExtensionConfigs,
|
||||
widgetHelper.addProvider,
|
||||
widgetHelper.addEdition,
|
||||
widgetHelper.addContext,
|
||||
widgetHelper.filterConfigs
|
||||
];
|
||||
|
||||
private readonly _gridModifiers: Array<(item: Array<WidgetConfig>, originalConfig: Array<WidgetConfig>) => Array<WidgetConfig>> = [
|
||||
widgetHelper.validateGridConfig
|
||||
];
|
||||
|
||||
@ViewChildren(DashboardTab) private _tabs: QueryList<DashboardTab>;
|
||||
@ViewChild(PanelComponent) private _panel: PanelComponent;
|
||||
constructor(
|
||||
@Inject(forwardRef(() => DashboardServiceInterface)) protected dashboardService: DashboardServiceInterface,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) protected _cd: ChangeDetectorRef
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
this.tabs = [];
|
||||
let innerTabIds = [];
|
||||
let allPosibleInnerTab = innerTabRegistry.innerTabs;
|
||||
let filteredTabs: IDashboardInnerTab[] = [];
|
||||
if (this.tab.content) {
|
||||
innerTabIds = Object.values(this.tab.content)[0];
|
||||
if (innerTabIds && innerTabIds.length > 0) {
|
||||
innerTabIds.forEach(tabId => {
|
||||
let tab = allPosibleInnerTab.find(i => i.id === tabId);
|
||||
filteredTabs.push(tab);
|
||||
});
|
||||
this.loadNewTabs(filteredTabs);
|
||||
}
|
||||
this._cd.detectChanges();
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterContentInit(): void {
|
||||
if (this._tabs) {
|
||||
this._tabs.forEach(tabContent => {
|
||||
this._register(tabContent.onResize(() => {
|
||||
this._onResize.fire();
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.dispose();
|
||||
}
|
||||
|
||||
private loadNewTabs(dashboardTabs: IDashboardInnerTab[]) {
|
||||
if (dashboardTabs && dashboardTabs.length > 0) {
|
||||
let selectedTabs = dashboardTabs.map(v => {
|
||||
|
||||
if (Object.keys(v.content).length !== 1) {
|
||||
error('Exactly 1 content must be defined per space');
|
||||
}
|
||||
|
||||
let key = Object.keys(v.content)[0];
|
||||
if (key === WIDGETS_TAB) {
|
||||
let configs = <WidgetConfig[]>Object.values(v.content)[0];
|
||||
this._configModifiers.forEach(cb => {
|
||||
configs = cb.apply(this, [configs, this.dashboardService, this.tab.context]);
|
||||
});
|
||||
this._gridModifiers.forEach(cb => {
|
||||
configs = cb.apply(this, [configs]);
|
||||
});
|
||||
return { id: v.id, title: v.title, content: { 'widgets-tab': configs } };
|
||||
}
|
||||
return { id: v.id, title: v.title, content: v.content };
|
||||
}).map(v => {
|
||||
let config = v as TabConfig;
|
||||
config.context = this.tab.context;
|
||||
config.editable = false;
|
||||
config.canClose = false;
|
||||
this.addNewTab(config);
|
||||
return config;
|
||||
});
|
||||
|
||||
// put this immediately on the stack so that is ran *after* the tab is rendered
|
||||
setTimeout(() => {
|
||||
this._panel.selectTab(selectedTabs.pop().id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private addNewTab(tab: TabConfig): void {
|
||||
let existedTab = this.tabs.find(i => i.id === tab.id);
|
||||
if (!existedTab) {
|
||||
this.tabs.push(tab);
|
||||
this._cd.detectChanges();
|
||||
}
|
||||
}
|
||||
|
||||
private getContentType(tab: TabConfig): string {
|
||||
return tab.content ? Object.keys(tab.content)[0] : '';
|
||||
}
|
||||
|
||||
public get id(): string {
|
||||
return this.tab.id;
|
||||
}
|
||||
|
||||
public get editable(): boolean {
|
||||
return this.tab.editable;
|
||||
}
|
||||
|
||||
public layout() {
|
||||
|
||||
}
|
||||
|
||||
public refresh(): void {
|
||||
if (this._tabs) {
|
||||
this._tabs.forEach(tabContent => {
|
||||
tabContent.refresh();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public enableEdit(): void {
|
||||
if (this._tabs) {
|
||||
this._tabs.forEach(tabContent => {
|
||||
tabContent.enableEdit();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public handleTabChange(tab: TabComponent): void {
|
||||
let localtab = this._tabs.find(i => i.id === tab.identifier);
|
||||
localtab.layout();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import * as nls from 'vs/nls';
|
||||
|
||||
import { registerTabContent } from 'sql/platform/dashboard/common/dashboardRegistry';
|
||||
|
||||
export const LEFT_NAV_TAB = 'left-nav-bar';
|
||||
|
||||
let leftNavSchema: IJSONSchema = {
|
||||
type: 'array',
|
||||
description: nls.localize('dashboard.tab.content.left-nav-bar', "The list of inner tabs IDs that will be displayed in this vertical navigation bar."),
|
||||
items: {
|
||||
type: 'string'
|
||||
}
|
||||
};
|
||||
|
||||
registerTabContent(LEFT_NAV_TAB, leftNavSchema);
|
||||
10
src/sql/parts/dashboard/tabs/dashboardLeftNavBar.css
Normal file
10
src/sql/parts/dashboard/tabs/dashboardLeftNavBar.css
Normal file
@@ -0,0 +1,10 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
dashboard-left-nav-bar {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
@@ -6,8 +6,9 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import * as nls from 'vs/nls';
|
||||
|
||||
import { registerTabContent } from 'sql/platform/dashboard/common/dashboardRegistry';
|
||||
import { registerInnerTabContent } from 'sql/platform/dashboard/common/innerTabRegistry';
|
||||
|
||||
export const WEBVIEW_TABS = 'webview-tab';
|
||||
export const WEBVIEW_TAB = 'webview-tab';
|
||||
|
||||
let webviewSchema: IJSONSchema = {
|
||||
type: 'null',
|
||||
@@ -15,4 +16,5 @@ let webviewSchema: IJSONSchema = {
|
||||
default: null
|
||||
};
|
||||
|
||||
registerTabContent(WEBVIEW_TABS, webviewSchema);
|
||||
registerTabContent(WEBVIEW_TAB, webviewSchema);
|
||||
registerInnerTabContent(WEBVIEW_TAB, webviewSchema);
|
||||
@@ -7,8 +7,9 @@ import * as nls from 'vs/nls';
|
||||
|
||||
import { generateDashboardWidgetSchema } from 'sql/parts/dashboard/pages/dashboardPageContribution';
|
||||
import { registerTabContent } from 'sql/platform/dashboard/common/dashboardRegistry';
|
||||
import { registerInnerTabContent } from 'sql/platform/dashboard/common/innerTabRegistry';
|
||||
|
||||
export const WIDGETS_TABS = 'widgets-tab';
|
||||
export const WIDGETS_TAB = 'widgets-tab';
|
||||
|
||||
let widgetsSchema: IJSONSchema = {
|
||||
type: 'array',
|
||||
@@ -16,4 +17,5 @@ let widgetsSchema: IJSONSchema = {
|
||||
items: generateDashboardWidgetSchema(undefined, true)
|
||||
};
|
||||
|
||||
registerTabContent(WIDGETS_TABS, widgetsSchema);
|
||||
registerTabContent(WIDGETS_TAB, widgetsSchema);
|
||||
registerInnerTabContent(WIDGETS_TAB, widgetsSchema);
|
||||
|
||||
@@ -34,6 +34,7 @@ export interface IDashboardRegistry {
|
||||
registerDashboardProvider(id: string, properties: ProviderProperties): void;
|
||||
getProperties(id: string): ProviderProperties;
|
||||
registerTab(tab: IDashboardTab): void;
|
||||
registerTabContent(id: string, schema: IJSONSchema): void;
|
||||
tabs: Array<IDashboardTab>;
|
||||
tabContentSchemaProperties: IJSONSchemaMap;
|
||||
}
|
||||
|
||||
70
src/sql/platform/dashboard/common/innerTabRegistry.ts
Normal file
70
src/sql/platform/dashboard/common/innerTabRegistry.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
|
||||
import { Extensions as ConfigurationExtension } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { deepClone } from 'vs/base/common/objects';
|
||||
|
||||
import { WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||
|
||||
export const Extensions = {
|
||||
InnerTabContributions: 'dashboard.contributions.innerTabs'
|
||||
};
|
||||
|
||||
export interface IDashboardInnerTab {
|
||||
id: string;
|
||||
title: string;
|
||||
hasIcon: boolean;
|
||||
content?: object;
|
||||
}
|
||||
|
||||
export interface IDashboardInnerTabRegistry {
|
||||
registerInnerTab(tab: IDashboardInnerTab): void;
|
||||
registerInnerTabContent(id: string, schema: IJSONSchema): void;
|
||||
innerTabs: Array<IDashboardInnerTab>;
|
||||
innerTabContentSchemaProperties: IJSONSchemaMap;
|
||||
}
|
||||
|
||||
class DashboardInnerTabRegistry implements IDashboardInnerTabRegistry {
|
||||
private _innertabs = new Array<IDashboardInnerTab>();
|
||||
private _dashboardInnerTabContentSchemaProperties: IJSONSchemaMap = {};
|
||||
|
||||
public registerInnerTab(tab: IDashboardInnerTab): void {
|
||||
this._innertabs.push(tab);
|
||||
}
|
||||
|
||||
public get innerTabs(): Array<IDashboardInnerTab> {
|
||||
return this._innertabs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a dashboard widget
|
||||
* @param id id of the widget
|
||||
* @param schema config schema of the widget
|
||||
*/
|
||||
public registerInnerTabContent(id: string, schema: IJSONSchema): void {
|
||||
this._dashboardInnerTabContentSchemaProperties[id] = schema;
|
||||
}
|
||||
|
||||
public get innerTabContentSchemaProperties(): IJSONSchemaMap {
|
||||
return deepClone(this._dashboardInnerTabContentSchemaProperties);
|
||||
}
|
||||
}
|
||||
|
||||
const dashboardInnerTabRegistry = new DashboardInnerTabRegistry();
|
||||
Registry.add(Extensions.InnerTabContributions, dashboardInnerTabRegistry);
|
||||
|
||||
export function registerInnerTab(innerTab: IDashboardInnerTab): void {
|
||||
dashboardInnerTabRegistry.registerInnerTab(innerTab);
|
||||
}
|
||||
|
||||
export function registerInnerTabContent(id: string, schema: IJSONSchema): void {
|
||||
dashboardInnerTabRegistry.registerInnerTabContent(id, schema);
|
||||
}
|
||||
|
||||
export function generateInnerTabContentSchemaProperties(): IJSONSchemaMap {
|
||||
return dashboardInnerTabRegistry.innerTabContentSchemaProperties;
|
||||
}
|
||||
@@ -160,9 +160,11 @@ import 'sql/parts/dashboard/widgets/tasks/tasksWidget.contribution';
|
||||
import 'sql/parts/dashboard/widgets/webview/webviewWidget.contribution';
|
||||
import 'sql/parts/dashboard/dashboardConfig.contribution';
|
||||
/* Tabs */
|
||||
import 'sql/parts/dashboard/tabs/dashboardLeftNavBar.contribution';
|
||||
import 'sql/parts/dashboard/tabs/dashboardWebviewTab.contribution';
|
||||
import 'sql/parts/dashboard/tabs/dashboardWidgetTab.contribution';
|
||||
import 'sql/parts/dashboard/tabs/dashboardGridTab.contribution';
|
||||
import 'sql/parts/dashboard/tabs/dashboardWidgetTab.contribution';
|
||||
import 'sql/parts/dashboard/tabs/dashboardInnerTab.contribution';
|
||||
import 'sql/parts/dashboard/common/dashboardTab.contribution';
|
||||
/* Tasks */
|
||||
import 'sql/workbench/common/actions.contribution';
|
||||
|
||||
Reference in New Issue
Block a user