Feat/model backed ui (#1145)

This is an initial PR for a new model-driven UI where extensions can provide definitions of the components & how they're laid out using Containers.
#1140, #1141, #1142, #1143 and #1144 are all tracking additional work needed to improve the initial implementation and fix some issues with the implementation.

Features:
- Supports defining a FlexContainer that maps to a flexbox-based layout.
- Supports creating a card component, which is a key-value pair based control that will lay out simple information to a user. Eventually this will have an optional set of actions associated with it.
- Has a sample project which shows how to use the API and was used for verification
This commit is contained in:
Kevin Cunnane
2018-04-13 15:59:18 -07:00
committed by GitHub
parent e022f4a0d1
commit b2c70e9301
63 changed files with 13238 additions and 84 deletions

View File

@@ -14,6 +14,7 @@ import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboar
import { WIDGETS_CONTAINER } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.contribution';
import { GRID_CONTAINER } from 'sql/parts/dashboard/containers/dashboardGridContainer.contribution';
import { WEBVIEW_CONTAINER } from 'sql/parts/dashboard/containers/dashboardWebviewContainer.contribution';
import { MODELVIEW_CONTAINER } from 'sql/parts/dashboard/containers/dashboardModelViewContainer.contribution';
import { CONTROLHOST_CONTAINER } from 'sql/parts/dashboard/containers/dashboardControlHostContainer.contribution';
import { NAV_SECTION } from 'sql/parts/dashboard/containers/dashboardNavSection.contribution';
import { IDashboardContainerRegistry, Extensions as DashboardContainerExtensions, IDashboardContainer, registerContainerType } from 'sql/platform/dashboard/common/dashboardContainerRegistry';
@@ -25,6 +26,7 @@ const containerTypes = [
WIDGETS_CONTAINER,
GRID_CONTAINER,
WEBVIEW_CONTAINER,
MODELVIEW_CONTAINER,
CONTROLHOST_CONTAINER,
NAV_SECTION
];

View File

@@ -13,6 +13,8 @@
</dashboard-webview-container>
<dashboard-widget-container *ngIf="getContentType(tab) === 'widgets-container'" [tab]="tab">
</dashboard-widget-container>
<dashboard-modelview-container *ngIf="getContentType(tab) === 'modelview-container'" [tab]="tab">
</dashboard-modelview-container>
<dashboard-controlhost-container *ngIf="getContentType(tab) === 'controlhost-container'" [tab]="tab">
</dashboard-controlhost-container>
<dashboard-nav-section *ngIf="getContentType(tab) === 'nav-section'" [tab]="tab">

View File

@@ -39,7 +39,7 @@ export class DashboardErrorContainer extends DashboardTab implements AfterViewIn
ngAfterViewInit() {
let errorMessage = this._errorMessageContainer.nativeElement as HTMLElement;
errorMessage.innerHTML = nls.localize('dashboardNavSection_loadTabError', 'The {0} has an invalid content. Please contact extension owner.', this.tab.title);
errorMessage.innerHTML = nls.localize('dashboardNavSection_loadTabError', 'The "{0}" section has invalid content. Please contact extension owner.', this.tab.title);
}
public get id(): string {

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 { ModelViewContent } from 'sql/parts/modelComponents/modelViewContent.component';
@Component({
selector: 'dashboard-modelview-container',
providers: [{ provide: DashboardTab, useExisting: forwardRef(() => DashboardModelViewContainer) }],
template: `
<modelview-content [modelViewId]="tab.id">
</modelview-content>
`
})
export class DashboardModelViewContainer extends DashboardTab implements AfterContentInit {
@Input() private tab: TabConfig;
private _onResize = new Emitter<void>();
public readonly onResize: Event<void> = this._onResize.event;
@ViewChild(ModelViewContent) private _modelViewContent: ModelViewContent;
constructor() {
super();
}
ngAfterContentInit(): void {
this._register(this._modelViewContent.onResize(() => {
this._onResize.fire();
}));
}
public layout(): void {
this._modelViewContent.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,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 { registerContainerType, registerNavSectionContainerType } from 'sql/platform/dashboard/common/dashboardContainerRegistry';
export const MODELVIEW_CONTAINER = 'modelview-container';
let modelviewSchema: IJSONSchema = {
type: 'null',
description: nls.localize('dashboard.container.modelview', "The model-backed view that will be displayed in this tab."),
default: null
};
registerContainerType(MODELVIEW_CONTAINER, modelviewSchema);
registerNavSectionContainerType(MODELVIEW_CONTAINER, modelviewSchema);

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

View File

@@ -11,6 +11,8 @@
</dashboard-webview-container>
<dashboard-widget-container *ngIf="getContentType(tab) === 'widgets-container'" [tab]="tab">
</dashboard-widget-container>
<dashboard-modelview-container *ngIf="getContentType(tab) === 'modelview-container'" [tab]="tab">
</dashboard-modelview-container>
<dashboard-grid-container *ngIf="getContentType(tab) === 'grid-container'" [tab]="tab">
</dashboard-grid-container>
<dashboard-error-container *ngIf="getContentType(tab) === 'error-container'" [tab]="tab">

View File

@@ -16,7 +16,7 @@ import { memoize } from 'vs/base/common/decorators';
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
import { TabConfig } from 'sql/parts/dashboard/common/dashboardWidget';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { IDashboardWebview } from 'sql/services/dashboardWebview/common/dashboardWebviewService';
import { IDashboardWebview } from 'sql/services/dashboard/common/dashboardViewService';
import { AngularDisposable } from 'sql/base/common/lifecycle';
import * as sqlops from 'sqlops';
@@ -46,7 +46,7 @@ export class WebviewContent extends AngularDisposable implements OnInit, IDashbo
}
ngOnInit() {
this._dashboardService.dashboardWebviewService.registerWebview(this);
this._dashboardService.dashboardViewService.registerWebview(this);
this._createWebview();
this._register(addDisposableListener(window, EventType.RESIZE, e => {
this.layout();

View File

@@ -14,6 +14,7 @@ import { ChartsModule } from 'ng2-charts/ng2-charts';
import CustomUrlSerializer from 'sql/common/urlSerializer';
import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService';
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
import { Extensions as ComponentExtensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
import { Registry } from 'vs/platform/registry/common/platform';
@@ -35,9 +36,12 @@ import { DashboardWidgetWrapper } from 'sql/parts/dashboard/contents/dashboardWi
import { DashboardWidgetContainer } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.component';
import { DashboardGridContainer } from 'sql/parts/dashboard/containers/dashboardGridContainer.component';
import { DashboardWebviewContainer } from 'sql/parts/dashboard/containers/dashboardWebviewContainer.component';
import { DashboardModelViewContainer } from 'sql/parts/dashboard/containers/dashboardModelViewContainer.component';
import { DashboardErrorContainer } from 'sql/parts/dashboard/containers/dashboardErrorContainer.component';
import { DashboardNavSection } from 'sql/parts/dashboard/containers/dashboardNavSection.component';
import { WidgetContent } from 'sql/parts/dashboard/contents/widgetContent.component';
import { ModelViewContent } from 'sql/parts/modelComponents/modelViewContent.component';
import { ModelComponentWrapper } from 'sql/parts/modelComponents/modelComponentWrapper.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';
@@ -49,9 +53,9 @@ import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.comp
import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component';
let baseComponents = [DashboardHomeContainer, DashboardComponent, DashboardWidgetWrapper, DashboardWebviewContainer,
DashboardWidgetContainer, DashboardGridContainer, DashboardErrorContainer, DashboardNavSection, WebviewContent, WidgetContent,
DashboardWidgetContainer, DashboardGridContainer, DashboardErrorContainer, DashboardNavSection, ModelViewContent, WebviewContent, WidgetContent,
ComponentHostDirective, BreadcrumbComponent, ControlHostContent, DashboardControlHostContainer,
JobsViewComponent, AgentViewComponent, JobHistoryComponent, JobStepsViewComponent];
JobsViewComponent, AgentViewComponent, JobHistoryComponent, JobStepsViewComponent, DashboardModelViewContainer, ModelComponentWrapper];
/* Panel */
import { PanelModule } from 'sql/base/browser/ui/panel/panel.module';
@@ -81,6 +85,9 @@ let widgetComponents = [
/* Insights */
let insightComponents = Registry.as<IInsightRegistry>(Extensions.InsightContribution).getAllCtors();
/* Model-backed components */
let extensionComponents = Registry.as<IComponentRegistry>(ComponentExtensions.ComponentContribution).getAllCtors();
// Setup routes for various child components
const appRoutes: Routes = [
{ path: 'database-dashboard', component: DatabaseDashboardPage },
@@ -99,13 +106,15 @@ const appRoutes: Routes = [
...baseComponents,
...pageComponents,
...widgetComponents,
...insightComponents
...insightComponents,
...extensionComponents
],
// also for widgets
entryComponents: [
DashboardComponent,
...widgetComponents,
...insightComponents
...insightComponents,
...extensionComponents
],
imports: [
CommonModule,

View File

@@ -23,7 +23,7 @@ import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { AngularEventType, IAngularEvent, IAngularEventingService } from 'sql/services/angularEventing/angularEventingService';
import { IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
import { TabSettingConfig } from 'sql/parts/dashboard/common/dashboardWidget';
import { IDashboardWebviewService } from 'sql/services/dashboardWebview/common/dashboardWebviewService';
import { IDashboardViewService } from 'sql/services/dashboard/common/dashboardViewService';
import { AngularDisposable } from 'sql/base/common/lifecycle';
import { ConnectionContextkey } from 'sql/parts/connection/common/connectionContextKey';
@@ -138,7 +138,7 @@ export class DashboardServiceInterface extends AngularDisposable {
private _capabilitiesService = this._bootstrapService.capabilitiesService;
private _configurationEditingService = this._bootstrapService.configurationEditorService;
private _commandService = this._bootstrapService.commandService;
private _dashboardWebviewService = this._bootstrapService.dashboardWebviewService;
private _dashboardViewService = this._bootstrapService.dashboardViewService;
private _partService = this._bootstrapService.partService;
private _angularEventingService = this._bootstrapService.angularEventingService;
private _environmentService = this._bootstrapService.environmentService;
@@ -211,8 +211,8 @@ export class DashboardServiceInterface extends AngularDisposable {
return this._instantiationService;
}
public get dashboardWebviewService(): IDashboardWebviewService {
return this._dashboardWebviewService;
public get dashboardViewService(): IDashboardViewService {
return this._dashboardViewService;
}
public get partService(): IPartService {

View File

@@ -204,6 +204,8 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget,
}
componentInstance.data = { columns: result.columnInfo.map(item => item.columnName), rows: result.rows.map(row => row.map(item => item.displayValue)) };
componentInstance.data = { columns: result.columnInfo.map(item => item.columnName), rows: result.rows.map(row => row.map(item => item.displayValue)) };
if (componentInstance.init) {
componentInstance.init();
}

View File

@@ -13,7 +13,7 @@ import { memoize } from 'vs/base/common/decorators';
import { DashboardWidget, IDashboardWidget, WidgetConfig, WIDGET_CONFIG } from 'sql/parts/dashboard/common/dashboardWidget';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { IDashboardWebview } from 'sql/services/dashboardWebview/common/dashboardWebviewService';
import { IDashboardWebview } from 'sql/services/dashboard/common/dashboardViewService';
import * as sqlops from 'sqlops';
@@ -47,7 +47,7 @@ export class WebviewWidget extends DashboardWidget implements IDashboardWidget,
}
ngOnInit() {
this._dashboardService.dashboardWebviewService.registerWebview(this);
this._dashboardService.dashboardViewService.registerWebview(this);
this._createWebview();
}