mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
initial check in for dashboard grid layout (#740)
dashboard grid layout container
This commit is contained in:
@@ -16,6 +16,8 @@
|
|||||||
</dashboard-webview-tab>
|
</dashboard-webview-tab>
|
||||||
<dashboard-widget-tab *ngIf="getContentType(tab) === 'widgets-tab'" [tab]="tab">
|
<dashboard-widget-tab *ngIf="getContentType(tab) === 'widgets-tab'" [tab]="tab">
|
||||||
</dashboard-widget-tab>
|
</dashboard-widget-tab>
|
||||||
|
<dashboard-grid-tab *ngIf="getContentType(tab) === 'grid-tab'" [tab]="tab">
|
||||||
|
</dashboard-grid-tab>
|
||||||
</tab>
|
</tab>
|
||||||
</panel>
|
</panel>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { AngularEventType, IAngularEvent } from 'sql/services/angularEventing/an
|
|||||||
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
|
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
|
||||||
import { error } from 'sql/base/common/log';
|
import { error } from 'sql/base/common/log';
|
||||||
import { WIDGETS_TABS } from 'sql/parts/dashboard/tabs/dashboardWidgetTab.contribution';
|
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 { WEBVIEW_TABS } from 'sql/parts/dashboard/tabs/dashboardWebviewTab.contribution';
|
||||||
|
|
||||||
import { Registry } from 'vs/platform/registry/common/platform';
|
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];
|
let key = Object.keys(v.content)[0];
|
||||||
if (key === WIDGETS_TABS) {
|
if (key === WIDGETS_TABS || key === GRID_TABS) {
|
||||||
let configs = <WidgetConfig[]>Object.values(v.content)[0];
|
let configs = <WidgetConfig[]>Object.values(v.content)[0];
|
||||||
this._configModifiers.forEach(cb => {
|
this._configModifiers.forEach(cb => {
|
||||||
configs = cb.apply(this, [configs]);
|
configs = cb.apply(this, [configs]);
|
||||||
@@ -263,7 +264,12 @@ export abstract class DashboardPage extends Disposable implements OnDestroy {
|
|||||||
this._gridModifiers.forEach(cb => {
|
this._gridModifiers.forEach(cb => {
|
||||||
configs = cb.apply(this, [configs]);
|
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;
|
return v;
|
||||||
}).map(v => {
|
}).map(v => {
|
||||||
|
|||||||
@@ -28,12 +28,13 @@ import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost
|
|||||||
import { DashboardComponent, DASHBOARD_SELECTOR } from 'sql/parts/dashboard/dashboard.component';
|
import { DashboardComponent, DASHBOARD_SELECTOR } from 'sql/parts/dashboard/dashboard.component';
|
||||||
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/common/dashboardWidgetWrapper.component';
|
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/common/dashboardWidgetWrapper.component';
|
||||||
import { DashboardWidgetTab } from 'sql/parts/dashboard/tabs/dashboardWidgetTab.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 { DashboardWebviewTab } from 'sql/parts/dashboard/tabs/dashboardWebviewTab.component';
|
||||||
import { WidgetContent } from 'sql/parts/dashboard/contents/widgetContent.component';
|
import { WidgetContent } from 'sql/parts/dashboard/contents/widgetContent.component';
|
||||||
import { WebviewContent } from 'sql/parts/dashboard/contents/webviewContent.component';
|
import { WebviewContent } from 'sql/parts/dashboard/contents/webviewContent.component';
|
||||||
import { BreadcrumbComponent } from 'sql/base/browser/ui/breadcrumb/breadcrumb.component';
|
import { BreadcrumbComponent } from 'sql/base/browser/ui/breadcrumb/breadcrumb.component';
|
||||||
import { IBreadcrumbService } from 'sql/base/browser/ui/breadcrumb/interfaces';
|
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 */
|
/* Panel */
|
||||||
import { PanelModule } from 'sql/base/browser/ui/panel/panel.module';
|
import { PanelModule } from 'sql/base/browser/ui/panel/panel.module';
|
||||||
|
|||||||
@@ -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 {
|
export function generateDashboardTabSchema(type?: 'database' | 'server'): IJSONSchema {
|
||||||
return {
|
return {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
|||||||
25
src/sql/parts/dashboard/tabs/dashboardGridTab.component.html
Normal file
25
src/sql/parts/dashboard/tabs/dashboardGridTab.component.html
Normal 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>
|
||||||
191
src/sql/parts/dashboard/tabs/dashboardGridTab.component.ts
Normal file
191
src/sql/parts/dashboard/tabs/dashboardGridTab.component.ts
Normal 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!./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<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.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 {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
25
src/sql/parts/dashboard/tabs/dashboardGridTab.css
Normal file
25
src/sql/parts/dashboard/tabs/dashboardGridTab.css
Normal 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;
|
||||||
|
}
|
||||||
@@ -162,6 +162,7 @@ import 'sql/parts/dashboard/dashboardConfig.contribution';
|
|||||||
/* Tabs */
|
/* Tabs */
|
||||||
import 'sql/parts/dashboard/tabs/dashboardWebviewTab.contribution';
|
import 'sql/parts/dashboard/tabs/dashboardWebviewTab.contribution';
|
||||||
import 'sql/parts/dashboard/tabs/dashboardWidgetTab.contribution';
|
import 'sql/parts/dashboard/tabs/dashboardWidgetTab.contribution';
|
||||||
|
import 'sql/parts/dashboard/tabs/dashboardGridTab.contribution';
|
||||||
import 'sql/parts/dashboard/common/dashboardTab.contribution';
|
import 'sql/parts/dashboard/common/dashboardTab.contribution';
|
||||||
/* Tasks */
|
/* Tasks */
|
||||||
import 'sql/workbench/common/actions.contribution';
|
import 'sql/workbench/common/actions.contribution';
|
||||||
|
|||||||
Reference in New Issue
Block a user