diff --git a/src/sql/parts/dashboard/common/dashboardPage.component.html b/src/sql/parts/dashboard/common/dashboardPage.component.html index 0845ee98e1..dac1282f03 100644 --- a/src/sql/parts/dashboard/common/dashboardPage.component.html +++ b/src/sql/parts/dashboard/common/dashboardPage.component.html @@ -16,6 +16,8 @@ + + diff --git a/src/sql/parts/dashboard/common/dashboardPage.component.ts b/src/sql/parts/dashboard/common/dashboardPage.component.ts index 4e174acaa2..f37849b30f 100644 --- a/src/sql/parts/dashboard/common/dashboardPage.component.ts +++ b/src/sql/parts/dashboard/common/dashboardPage.component.ts @@ -24,6 +24,7 @@ import { AngularEventType, IAngularEvent } from 'sql/services/angularEventing/an 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 { Registry } from 'vs/platform/registry/common/platform'; @@ -255,7 +256,7 @@ export abstract class DashboardPage extends Disposable implements OnDestroy { } let key = Object.keys(v.content)[0]; - if (key === WIDGETS_TABS) { + if (key === WIDGETS_TABS || key === GRID_TABS) { let configs = Object.values(v.content)[0]; this._configModifiers.forEach(cb => { configs = cb.apply(this, [configs]); @@ -263,7 +264,12 @@ export abstract class DashboardPage extends Disposable implements OnDestroy { this._gridModifiers.forEach(cb => { configs = cb.apply(this, [configs]); }); - return { id: v.id, title: v.title, content: { 'widgets-tab': configs }, alwaysShow: v.alwaysShow }; + if (key === WIDGETS_TABS) { + return { id: v.id, title: v.title, content: { 'widgets-tab': configs }, alwaysShow: v.alwaysShow }; + + } else { + return { id: v.id, title: v.title, content: { 'grid-tab': configs }, alwaysShow: v.alwaysShow }; + } } return v; }).map(v => { diff --git a/src/sql/parts/dashboard/dashboard.module.ts b/src/sql/parts/dashboard/dashboard.module.ts index f01373543a..ad0500c1b6 100644 --- a/src/sql/parts/dashboard/dashboard.module.ts +++ b/src/sql/parts/dashboard/dashboard.module.ts @@ -28,12 +28,13 @@ import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost import { DashboardComponent, DASHBOARD_SELECTOR } from 'sql/parts/dashboard/dashboard.component'; import { DashboardWidgetWrapper } from 'sql/parts/dashboard/common/dashboardWidgetWrapper.component'; 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 { 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, WidgetContent, WebviewContent, ComponentHostDirective, BreadcrumbComponent]; +let baseComponents = [DashboardComponent, DashboardWidgetWrapper, DashboardWebviewTab, DashboardWidgetTab, DashboardGridTab, WidgetContent, WebviewContent, ComponentHostDirective, BreadcrumbComponent]; /* Panel */ import { PanelModule } from 'sql/base/browser/ui/panel/panel.module'; diff --git a/src/sql/parts/dashboard/pages/dashboardPageContribution.ts b/src/sql/parts/dashboard/pages/dashboardPageContribution.ts index 6e8814f222..220d96428b 100644 --- a/src/sql/parts/dashboard/pages/dashboardPageContribution.ts +++ b/src/sql/parts/dashboard/pages/dashboardPageContribution.ts @@ -83,6 +83,55 @@ export function generateDashboardWidgetSchema(type?: 'database' | 'server', exte }; } +export function generateDashboardGridLayoutSchema(type?: 'database' | 'server', extension?: boolean): IJSONSchema { + let schemas; + if (extension) { + let extensionSchemas = type === 'server' ? widgetRegistry.serverWidgetSchema.extensionProperties : type === 'database' ? widgetRegistry.databaseWidgetSchema.extensionProperties : widgetRegistry.allSchema.extensionProperties; + schemas = type === 'server' ? widgetRegistry.serverWidgetSchema.properties : type === 'database' ? widgetRegistry.databaseWidgetSchema.properties : widgetRegistry.allSchema.properties; + schemas = mixin(schemas, extensionSchemas, true); + } else { + schemas = type === 'server' ? widgetRegistry.serverWidgetSchema.properties : type === 'database' ? widgetRegistry.databaseWidgetSchema.properties : widgetRegistry.allSchema.properties; + } + + return { + type: 'object', + properties: { + name: { + type: 'string' + }, + icon: { + type: 'string' + }, + provider: { + anyOf: [ + { + type: 'string' + }, + { + type: 'array', + items: { + type: 'string' + } + } + ] + }, + edition: { + anyOf: [ + { + type: 'number' + }, + { + type: 'array', + items: { + type: 'number' + } + } + ] + } + } + }; +} + export function generateDashboardTabSchema(type?: 'database' | 'server'): IJSONSchema { return { type: 'object', diff --git a/src/sql/parts/dashboard/tabs/dashboardGridTab.component.html b/src/sql/parts/dashboard/tabs/dashboardGridTab.component.html new file mode 100644 index 0000000000..b68f52a9ee --- /dev/null +++ b/src/sql/parts/dashboard/tabs/dashboardGridTab.component.html @@ -0,0 +1,25 @@ + +
+ + + + + + + + +
+
+ + + + + +
+
+
diff --git a/src/sql/parts/dashboard/tabs/dashboardGridTab.component.ts b/src/sql/parts/dashboard/tabs/dashboardGridTab.component.ts new file mode 100644 index 0000000000..3d995e4be0 --- /dev/null +++ b/src/sql/parts/dashboard/tabs/dashboardGridTab.component.ts @@ -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!./dashboardGridTab'; + +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/common/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-tab', + templateUrl: decodeURI(require.toUrl('sql/parts/dashboard/tabs/dashboardGridTab.component.html')), + providers: [{ provide: DashboardTab, useExisting: forwardRef(() => DashboardGridTab) }] +}) +export class DashboardGridTab extends DashboardTab implements OnDestroy, OnChanges { + @Input() private tab: TabConfig; + private _contents: GridCellConfig[]; + private _onResize = new Emitter(); + public readonly onResize: Event = 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 = 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 = 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; + 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.content) { + this._contents = Object.values(this.tab.content)[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 { + } +} diff --git a/src/sql/parts/dashboard/tabs/dashboardGridTab.contribution.ts b/src/sql/parts/dashboard/tabs/dashboardGridTab.contribution.ts new file mode 100644 index 0000000000..3f5cd4233c --- /dev/null +++ b/src/sql/parts/dashboard/tabs/dashboardGridTab.contribution.ts @@ -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_TABS = 'grid-tab'; + +let gridContentsSchema: 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_TABS, gridContentsSchema); diff --git a/src/sql/parts/dashboard/tabs/dashboardGridTab.css b/src/sql/parts/dashboard/tabs/dashboardGridTab.css new file mode 100644 index 0000000000..bfd9b141f0 --- /dev/null +++ b/src/sql/parts/dashboard/tabs/dashboardGridTab.css @@ -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; +} diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index 7d0aff5109..22f4a16b22 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -162,6 +162,7 @@ import 'sql/parts/dashboard/dashboardConfig.contribution'; /* Tabs */ 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/common/dashboardTab.contribution'; /* Tasks */ import 'sql/workbench/common/actions.contribution';