Refactor and rename dashboard tabs part 1 (#755)

* refactor and rename dashboard tabs

* undo incorrect changes
This commit is contained in:
Abbie Petchtes
2018-02-22 13:36:56 -08:00
committed by GitHub
parent 51b8e02455
commit dbc2ce0b3a
27 changed files with 199 additions and 200 deletions

View 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 { registerContainer, generateContainerTypeSchemaProperties } from 'sql/platform/dashboard/common/dashboardContainerRegistry';
export type IUserFriendlyIcon = string | { light: string; dark: string; };
export interface IDashboardContainerContrib {
id: string;
title: string;
icon?: IUserFriendlyIcon;
container: object;
}
const containerSchema: IJSONSchema = {
type: 'object',
properties: {
id: {
type: 'string',
description: localize('sqlops.extension.contributes.dashboard.container.id', "Unique identifier for this inner tab. Will be passed to the extension for any requests.")
},
icon: {
description: localize('sqlops.extension.contributes.dashboard.container.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.container.title', "Title of the inner tab to show the user.")
},
container: {
description: localize('sqlops.extension.contributes.dashboard.container.container', "The container that will be displayed in this inner tab."),
type: 'object',
properties: generateContainerTypeSchemaProperties()
}
}
};
const containerContributionSchema: IJSONSchema = {
description: localize('sqlops.extension.contributes.containers', "Contributes a single or multiple inner tabs for users to add to their dashboard."),
oneOf: [
containerSchema,
{
type: 'array',
items: containerSchema
}
]
};
ExtensionsRegistry.registerExtensionPoint<IDashboardContainerContrib | IDashboardContainerContrib[]>('dashboard.containers', [], containerContributionSchema).setHandler(extensions => {
function handleCommand(dashboardContainer: IDashboardContainerContrib, extension: IExtensionPointUser<any>) {
let { title, id, container, icon } = dashboardContainer;
if (!title) {
extension.collector.error('No title specified for extension.');
return;
}
if (!container) {
extension.collector.warn('No container 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()}")`);
}
}
registerContainer({ title, id, container, hasIcon: !!icon });
}
for (let extension of extensions) {
const { value } = extension;
if (Array.isArray<IDashboardContainerContrib>(value)) {
for (let command of value) {
handleCommand(command, extension);
}
} else {
handleCommand(value, extension);
}
}
});

View File

@@ -0,0 +1,25 @@
<!--
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-->
<div >
<table class="grid-table">
<tr *ngFor="let row of rows" class="grid-table-row">
<ng-container *ngFor="let col of cols">
<ng-container *ngIf="getContent(row,col) !== undefined">
<td class="table-cell" [colSpan]=getColspan(row,col) [rowSpan]=getRowspan(row,col) >
<div style="flex: 0 0 auto">
<dashboard-widget-wrapper *ngIf="isWidget(row,col)" [_config]="getWidgetContent(row,col)" style="position:absolute;">
</dashboard-widget-wrapper>
<webview-content *ngIf="isWebview(row,col)" [webviewId]="getWebviewId(row,col)">
</webview-content>
</div>
</td>
</ng-container>
</ng-container>
</tr>
</table>
</div>

View File

@@ -0,0 +1,191 @@
/*---------------------------------------------------------------------------------------------
* 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!./dashboardGridContainer';
import { Component, Inject, Input, forwardRef, ViewChild, ElementRef, ViewChildren, QueryList, OnDestroy, ChangeDetectorRef, EventEmitter, OnChanges } from '@angular/core';
import { NgGridConfig, NgGrid, NgGridItem } from 'angular2-grid';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { TabConfig, WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/contents/dashboardWidgetWrapper.component';
import { subscriptionToDisposable } from 'sql/base/common/lifecycle';
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import * as objects from 'vs/base/common/objects';
import Event, { Emitter } from 'vs/base/common/event';
import { concat } from 'rxjs/operator/concat';
export interface GridCellConfig {
id?: string;
row?: number;
col?: number;
colspan?: number;
rowspan?: number;
}
export interface GridWidgetConfig extends GridCellConfig, WidgetConfig {
}
export interface GridWebviewConfig extends GridCellConfig {
webview: {
id?: string;
};
}
@Component({
selector: 'dashboard-grid-container',
templateUrl: decodeURI(require.toUrl('sql/parts/dashboard/containers/dashboardGridContainer.component.html')),
providers: [{ provide: DashboardTab, useExisting: forwardRef(() => DashboardGridContainer) }]
})
export class DashboardGridContainer extends DashboardTab implements OnDestroy, OnChanges {
@Input() private tab: TabConfig;
private _contents: GridCellConfig[];
private _onResize = new Emitter<void>();
public readonly onResize: Event<void> = this._onResize.event;
protected SKELETON_WIDTH = 5;
protected rows: number[];
protected cols: number[];
protected getContent(row: number, col: number): GridCellConfig {
let widget = this._contents.filter(w => w.row === row && w.col === col);
return widget ? widget[0] : undefined;
}
protected getWidgetContent(row: number, col: number): GridWidgetConfig {
let content = this.getContent(row, col);
if (content) {
let widgetConfig = <GridWidgetConfig>content;
if (widgetConfig && widgetConfig.widget) {
return widgetConfig;
}
}
return undefined;
}
protected getWebviewContent(row: number, col: number): GridWebviewConfig {
let content = this.getContent(row, col);
if (content) {
let webviewConfig = <GridWebviewConfig>content;
if (webviewConfig && webviewConfig.webview) {
return webviewConfig;
}
}
return undefined;
}
protected isWidget(row: number, col: number): boolean {
let widgetConfig = this.getWidgetContent(row, col);
return widgetConfig !== undefined;
}
protected isWebview(row: number, col: number): boolean {
let webview = this.getWebviewContent(row, col);
return webview !== undefined;
}
protected getWebviewId(row: number, col: number): string {
let widgetConfig = this.getWebviewContent(row, col);
if (widgetConfig && widgetConfig.webview) {
return widgetConfig.webview.id;
}
return undefined;
}
protected getColspan(row: number, col: number): number {
let content = this.getContent(row, col);
let colspan: number = 1;
if (content && content.colspan) {
colspan = content.colspan;
}
return colspan;
}
protected getRowspan(row: number, col: number): number {
let content = this.getContent(row, col);
if (content && (content.rowspan)) {
return content.rowspan;
} else {
return 1;
}
}
@ViewChildren(DashboardWidgetWrapper) private _widgets: QueryList<DashboardWidgetWrapper>;
constructor(
@Inject(forwardRef(() => DashboardServiceInterface)) protected dashboardService: DashboardServiceInterface,
@Inject(forwardRef(() => ElementRef)) protected _el: ElementRef,
@Inject(forwardRef(() => ChangeDetectorRef)) protected _cd: ChangeDetectorRef
) {
super();
}
protected init() {
}
ngOnChanges() {
if (this.tab.container) {
this._contents = Object.values(this.tab.container)[0];
this._contents.forEach(widget => {
if (!widget.row) {
widget.row = 0;
}
if (!widget.col) {
widget.col = 0;
}
if (!widget.colspan) {
widget.colspan = 1;
}
if (!widget.rowspan) {
widget.rowspan = 1;
}
});
this.rows = this.createIndexes(this._contents.map(w => w.row));
this.cols = this.createIndexes(this._contents.map(w => w.col));
this._cd.detectChanges();
}
}
private createIndexes(indexes: number[]) {
let max = Math.max(...indexes) + 1;
return Array(max).fill(0).map((x, i) => i);
}
ngOnDestroy() {
this.dispose();
}
public get id(): string {
return this.tab.id;
}
public get editable(): boolean {
return this.tab.editable;
}
public layout() {
if (this._widgets) {
this._widgets.forEach(item => {
item.layout();
});
}
}
public refresh(): void {
if (this._widgets) {
this._widgets.forEach(item => {
item.refresh();
});
}
}
public enableEdit(): void {
}
}

View File

@@ -0,0 +1,19 @@
/*---------------------------------------------------------------------------------------------
* 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 { generateDashboardGridLayoutSchema } from 'sql/parts/dashboard/pages/dashboardPageContribution';
import { registerTabContent } from 'sql/platform/dashboard/common/dashboardRegistry';
export const GRID_CONTAINER = 'grid-container';
let gridContainersSchema: IJSONSchema = {
type: 'array',
description: nls.localize('dashboard.gridtab.content.items', "The list of widgets or webviews that will be displayed in this tab."),
items: generateDashboardGridLayoutSchema(undefined, true)
};
registerTabContent(GRID_CONTAINER, gridContainersSchema);

View File

@@ -0,0 +1,25 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
dashboard-tab {
height: auto;
width: auto;
}
.grid-table {
border-spacing: 5px;
}
.grid-table-row {
width: auto;
clear: both;
}
.table-cell {
height: 270px;
min-width: 300px;
vertical-align: top;
padding: 7px;
}

View File

@@ -0,0 +1,14 @@
<!--
/*---------------------------------------------------------------------------------------------
* 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-container *ngIf="getContentType(tab) === 'webview-container'" [tab]="tab">
</dashboard-webview-container>
<dashboard-widget-container *ngIf="getContentType(tab) === 'widgets-container'" [tab]="tab">
</dashboard-widget-container>
</tab>
</panel>

View 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!./dashboardNavSection';
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 { IDashboardContainerRegistry, Extensions as InnerTabExtensions, IDashboardContainer } from 'sql/platform/dashboard/common/dashboardContainerRegistry';
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_CONTAINER } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.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<IDashboardContainerRegistry>(InnerTabExtensions.dashboardContainerContributions);
@Component({
selector: 'dashboard-nav-section',
providers: [{ provide: DashboardTab, useExisting: forwardRef(() => DashboardNavSection) }],
templateUrl: decodeURI(require.toUrl('sql/parts/dashboard/containers/dashboardNavSection.component.html'))
})
export class DashboardNavSection 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.containers;
let filteredTabs: IDashboardContainer[] = [];
if (this.tab.container) {
innerTabIds = Object.values(this.tab.container)[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: IDashboardContainer[]) {
if (dashboardTabs && dashboardTabs.length > 0) {
let selectedTabs = dashboardTabs.map(v => {
if (Object.keys(v.container).length !== 1) {
error('Exactly 1 content must be defined per space');
}
let key = Object.keys(v.container)[0];
if (key === WIDGETS_CONTAINER) {
let configs = <WidgetConfig[]>Object.values(v.container)[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, container: { 'widgets-container': configs } };
}
return { id: v.id, title: v.title, container: v.container };
}).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.container ? Object.keys(tab.container)[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();
}
}

View File

@@ -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 NAV_SECTION = 'nav-section';
let NavSectionSchema: 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(NAV_SECTION, NavSectionSchema);

View 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-nav-section {
height: 100%;
width: 100%;
display: block;
}

View File

@@ -0,0 +1,55 @@
/*---------------------------------------------------------------------------------------------
* 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!./dashboardWebviewContainer';
import { Component, forwardRef, Input, AfterContentInit, ViewChild } from '@angular/core';
import Event, { Emitter } from 'vs/base/common/event';
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
import { TabConfig } from 'sql/parts/dashboard/common/dashboardWidget';
import { WebviewContent } from 'sql/parts/dashboard/contents/webviewContent.component';
@Component({
selector: 'dashboard-webview-container',
providers: [{ provide: DashboardTab, useExisting: forwardRef(() => DashboardWebviewContainer) }],
template: `
<webview-content [webviewId]="tab.id">
</webview-content>
`
})
export class DashboardWebviewContainer extends DashboardTab implements AfterContentInit {
@Input() private tab: TabConfig;
private _onResize = new Emitter<void>();
public readonly onResize: Event<void> = this._onResize.event;
@ViewChild(WebviewContent) private _webviewContent: WebviewContent;
constructor() {
super();
}
ngAfterContentInit(): void {
this._register(this._webviewContent.onResize(() => {
this._onResize.fire();
}));
}
public layout(): void {
this._webviewContent.layout();
}
public get id(): string {
return this.tab.id;
}
public get editable(): boolean {
return this.tab.editable;
}
public refresh(): void {
// no op
}
}

View File

@@ -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';
import { registerContainerType } from 'sql/platform/dashboard/common/dashboardContainerRegistry';
export const WEBVIEW_CONTAINER = 'webview-container';
let webviewSchema: IJSONSchema = {
type: 'null',
description: nls.localize('dashboard.tab.widgets', "The list of widgets that will be displayed in this tab."),
default: null
};
registerTabContent(WEBVIEW_CONTAINER, webviewSchema);
registerContainerType(WEBVIEW_CONTAINER, webviewSchema);

View 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-webview-container {
height: 100%;
width : 100%;
display: block;
}

View File

@@ -0,0 +1,80 @@
/*---------------------------------------------------------------------------------------------
* 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!./dashboardWidgetContainer';
import { Component, Inject, Input, forwardRef, ViewChild, ElementRef, ViewChildren, QueryList, OnDestroy, ChangeDetectorRef, EventEmitter, OnChanges, AfterContentInit } from '@angular/core';
import { NgGridConfig, NgGrid, NgGridItem } from 'angular2-grid';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { TabConfig, WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/contents/dashboardWidgetWrapper.component';
import { subscriptionToDisposable } from 'sql/base/common/lifecycle';
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
import { WidgetContent } from 'sql/parts/dashboard/contents/widgetContent.component';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import * as objects from 'vs/base/common/objects';
import Event, { Emitter } from 'vs/base/common/event';
@Component({
selector: 'dashboard-widget-container',
providers: [{ provide: DashboardTab, useExisting: forwardRef(() => DashboardWidgetContainer) }],
template: `
<widget-content [widgets]="widgets" [originalConfig]="tab.originalConfig" [context]="tab.context">
</widget-content>
`
})
export class DashboardWidgetContainer extends DashboardTab implements OnDestroy, OnChanges, AfterContentInit {
@Input() private tab: TabConfig;
private widgets: WidgetConfig[];
private _onResize = new Emitter<void>();
public readonly onResize: Event<void> = this._onResize.event;
@ViewChild(WidgetContent) private _widgetContent: WidgetContent;
constructor(
@Inject(forwardRef(() => ChangeDetectorRef)) protected _cd: ChangeDetectorRef
) {
super();
}
ngOnChanges() {
if (this.tab.container) {
this.widgets = Object.values(this.tab.container)[0];
this._cd.detectChanges();
}
}
ngAfterContentInit(): void {
this._register(this._widgetContent.onResize(() => {
this._onResize.fire();
}));
}
ngOnDestroy() {
this.dispose();
}
public get id(): string {
return this.tab.id;
}
public get editable(): boolean {
return this.tab.editable;
}
public layout() {
this._widgetContent.layout();
}
public refresh(): void {
this._widgetContent.layout();
}
public enableEdit(): void {
this._widgetContent.enableEdit();
}
}

View File

@@ -0,0 +1,21 @@
/*---------------------------------------------------------------------------------------------
* 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 { generateDashboardWidgetSchema } from 'sql/parts/dashboard/pages/dashboardPageContribution';
import { registerTabContent } from 'sql/platform/dashboard/common/dashboardRegistry';
import { registerContainerType } from 'sql/platform/dashboard/common/dashboardContainerRegistry';
export const WIDGETS_CONTAINER = 'widgets-container';
let widgetsSchema: IJSONSchema = {
type: 'array',
description: nls.localize('dashboard.tab.content.widgets', "The list of widgets that will be displayed in this tab."),
items: generateDashboardWidgetSchema(undefined, true)
};
registerTabContent(WIDGETS_CONTAINER, widgetsSchema);
registerContainerType(WIDGETS_CONTAINER, widgetsSchema);

View File

@@ -0,0 +1,9 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
dashboard-widget-container {
height: 100%;
width: 100%;
}