tasks widget updates (#9860)

* fix toolbar and remove tasks widget

* update refresh action

* remove contribution

* fix missing learn more menu item

* Alanren/refresh widgets new (#9863)

* refresh widgets

* dashboard refresh

* update
This commit is contained in:
Alan Ren
2020-04-06 12:25:09 -07:00
committed by GitHub
parent f95864ff82
commit 19a11ba94b
17 changed files with 131 additions and 373 deletions

View File

@@ -235,8 +235,10 @@ export class DashboardGridContainer extends DashboardTab implements OnDestroy {
widget.rowspan = '1';
}
});
this.rows = this.createIndexes(this._contents.map(w => w.row));
this.cols = this.createIndexes(this._contents.map(w => w.col));
if (this._contents.length > 0) {
this.rows = this.createIndexes(this._contents.map(w => w.row));
this.cols = this.createIndexes(this._contents.map(w => w.col));
}
}
}

View File

@@ -69,4 +69,9 @@ export class DashboardHomeContainer extends DashboardWidgetContainer {
this._scrollable.layout();
}
}
public refresh(): void {
super.refresh();
this._propertiesClass.refresh();
}
}

View File

@@ -20,7 +20,6 @@ import { AngularDisposable } from 'sql/base/browser/lifecycle';
/* Widgets */
import { PropertiesWidgetComponent } from 'sql/workbench/contrib/dashboard/browser/widgets/properties/propertiesWidget.component';
import { ExplorerWidget } from 'sql/workbench/contrib/dashboard/browser/widgets/explorer/explorerWidget.component';
import { TasksWidget } from 'sql/workbench/contrib/dashboard/browser/widgets/tasks/tasksWidget.component';
import { InsightsWidget } from 'sql/workbench/contrib/dashboard/browser/widgets/insights/insightsWidget.component';
import { WebviewWidget } from 'sql/workbench/contrib/dashboard/browser/widgets/webview/webviewWidget.component';
@@ -42,7 +41,6 @@ import { IColorTheme } from 'vs/platform/theme/common/themeService';
const componentMap: { [x: string]: Type<IDashboardWidget> } = {
'properties-widget': PropertiesWidgetComponent,
'explorer-widget': ExplorerWidget,
'tasks-widget': TasksWidget,
'insights-widget': InsightsWidget,
'webview-widget': WebviewWidget
};

View File

@@ -82,7 +82,7 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig
// tslint:disable:no-unused-variable
private readonly homeTabTitle: string = nls.localize('home', "Home");
private readonly homeTabId: string = 'homeTab';
private tabToolbarActionsConfig = new Map<string, WidgetConfig>();
private tabToolbarActionsConfig = new Map<string, any[]>();
private tabContents = new Map<string, string>();
static tabName = new RawContextKey<string>('tabName', undefined);
@@ -145,12 +145,7 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig
});
} else {
let tempWidgets = this.dashboardService.getSettings<Array<WidgetConfig>>([this.context, 'widgets'].join('.'));
// remove tasks widget because those will be shown in the toolbar
const index = tempWidgets.findIndex(c => c.widget['tasks-widget']);
if (index !== -1) {
tempWidgets.splice(index, 1);
}
this.processTasksWidgets(tempWidgets, this.homeTabId);
this._originalConfig = objects.deepClone(tempWidgets);
let properties = this.getProperties();
this._configModifiers.forEach((cb) => {
@@ -170,8 +165,6 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig
}
this.showToolbar = true;
const homeToolbarConfig = this.dashboardService.getSettings<Array<WidgetConfig>>([this.context, 'widgets'].join('.'))[0].widget['tasks-widget'];
this.tabToolbarActionsConfig.set(this.homeTabId, homeToolbarConfig);
this.createToolbar(this.toolbarContainer.nativeElement, this.homeTabId);
this._register(this.themeService.onDidColorThemeChange((event: IColorTheme) => {
@@ -179,58 +172,52 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig
}));
}
private getExtensionContributedHomeToolbarContent(content: ITaskbarContent[]): void {
let primary: IAction[] = [];
let secondary: IAction[] = [];
const menu = this.menuService.createMenu(MenuId.DashboardToolbar, this.contextKeyService);
let groups = menu.getActions({ arg: null, shouldForwardArgs: true });
fillInActions(groups, { primary, secondary }, false, (group: string) => group === undefined || group === '');
private getContributedTasks(tabId: string): ITaskbarContent[] {
const tasks: ITaskbarContent[] = [];
// for now we only allow contributing to the home tab toolbar.
if (tabId === this.homeTabId) {
let primary: IAction[] = [];
let secondary: IAction[] = [];
const menu = this.menuService.createMenu(MenuId.DashboardToolbar, this.contextKeyService);
let groups = menu.getActions({ arg: null, shouldForwardArgs: true });
fillInActions(groups, { primary, secondary }, false, (group: string) => group === undefined || group === '');
primary.forEach(a => {
if (a instanceof MenuItemAction) {
// Need to ensure that we don't add the same action multiple times
let foundIndex = firstIndex(content, act => act.action && act.action.id === a.id);
if (foundIndex < 0) {
content.push({ action: a });
primary.forEach(a => {
if (a instanceof MenuItemAction) {
// Need to ensure that we don't add the same action multiple times
let foundIndex = firstIndex(tasks, act => act.action && act.action.id === a.id);
if (foundIndex < 0) {
tasks.push({ action: a });
}
}
});
if (primary.length > 0) {
let separator: HTMLElement = Taskbar.createTaskbarSeparator();
tasks.push({ element: separator });
}
});
if (primary.length > 0) {
let separator: HTMLElement = Taskbar.createTaskbarSeparator();
content.push({ element: separator });
}
return tasks;
}
private hasExtensionContributedToolbarContent(): boolean {
let primary: IAction[] = [];
let secondary: IAction[] = [];
const menu = this.menuService.createMenu(MenuId.DashboardToolbar, this.contextKeyService);
let groups = menu.getActions({ arg: null, shouldForwardArgs: true });
fillInActions(groups, { primary, secondary }, false, (group: string) => group === undefined || group === '');
return primary.length > 0 || secondary.length > 0;
}
private createToolbar(parentElement: HTMLElement, tabName: string): void {
private createToolbar(parentElement: HTMLElement, tabId: string): void {
// clear out toolbar
DOM.clearNode(parentElement);
this.toolbar = this._register(new Taskbar(parentElement, { actionViewItemProvider: action => this.createActionItemProvider(action as Action) }));
let content = [];
content = this.getToolbarContent(this.tabToolbarActionsConfig.get(tabName));
if (tabName === this.homeTabId) {
content = this.getToolbarContent(tabId);
if (tabId === this.homeTabId) {
const configureDashboardCommand = MenuRegistry.getCommand('configureDashboard');
const configureDashboardAction = new ToolbarAction(configureDashboardCommand.id, configureDashboardCommand.title.toString(), TaskRegistry.getOrCreateTaskIconClassName(configureDashboardCommand), this.runAction, this, this.logService);
content.push({ action: configureDashboardAction });
}
this.toolbar.setContent(content);
}
private getToolbarContent(toolbarTasks: WidgetConfig): ITaskbarContent[] {
private getToolbarContent(tabId: string): ITaskbarContent[] {
const toolbarTasks = this.tabToolbarActionsConfig.get(tabId);
let tasks = TaskRegistry.getTasks();
let content;
let content = [];
if (types.isArray(toolbarTasks) && toolbarTasks.length > 0) {
tasks = toolbarTasks.map(i => {
if (types.isString(i)) {
@@ -250,9 +237,12 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig
}
// get extension actions contributed to the page's toolbar
this.getExtensionContributedHomeToolbarContent(content);
const contributedTasks = this.getContributedTasks(tabId);
content.push(...contributedTasks);
const refreshAction = new RefreshWidgetAction(this.refresh, this);
const refreshAction = new RefreshWidgetAction(() => {
this.refresh();
}, this);
content.push({ action: refreshAction });
return content;
}
@@ -456,12 +446,7 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig
configs = cb.apply(this, [configs]);
});
// remove tasks widget because the tasks will be shown in the toolbar
const index = configs.findIndex(c => c.widget['tasks-widget']);
if (index !== -1) {
this.tabToolbarActionsConfig.set(value.id, configs[index].widget['tasks-widget']);
configs.splice(index, 1);
}
this.processTasksWidgets(configs, value.id);
if (key === WIDGETS_CONTAINER) {
return { id: value.id, title: value.title, container: { 'widgets-container': configs }, alwaysShow: value.alwaysShow, iconClass: value.iconClass };
@@ -473,6 +458,28 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig
return { id: value.id, title: value.title, container: containerResult.container, alwaysShow: value.alwaysShow, iconClass: value.iconClass };
}
/**
* Process the tasks widgets, tasks widgets has been deprecated and the tasks are now in toolbar.
* @param widgets widgets
* @param tabId tab id
*/
private processTasksWidgets(widgets: WidgetConfig[], tabId: string): void {
let index;
const allTasks = [];
// do this in a while loop since there might be multiple tasks widgets in a tab
do {
index = widgets.findIndex(c => c.widget['tasks-widget']);
if (index !== -1) {
const tasks = widgets[index].widget['tasks-widget'];
if (Array.isArray(tasks)) {
allTasks.push(...tasks);
}
widgets.splice(index, 1);
}
} while (index !== -1);
this.tabToolbarActionsConfig.set(tabId, allTasks);
}
protected getContentType(tab: TabConfig): string {
return tab.container ? Object.keys(tab.container)[0] : '';
}
@@ -525,8 +532,7 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig
public handleTabChange(tab: TabComponent): void {
this._tabName.set(tab.identifier);
const tabContent = this.tabContents.get(tab.identifier);
if (tab.identifier === this.homeTabId || tabContent === WIDGETS_CONTAINER || tabContent === GRID_CONTAINER || tabContent === NAV_SECTION
|| this.hasExtensionContributedToolbarContent()) {
if (tab.identifier === this.homeTabId || tabContent === WIDGETS_CONTAINER || tabContent === GRID_CONTAINER || tabContent === NAV_SECTION) {
this.showToolbar = true;
this.createToolbar(this.toolbarContainer.nativeElement, tab.identifier);
} else { // hide toolbar
@@ -534,9 +540,6 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig
}
this._cd.detectChanges();
const localtab = this._tabs.find(i => i.id === tab.identifier);
this._editEnabled.fire(localtab.editable);
this._cd.detectChanges();
}
public handleTabClose(tab: TabComponent): void {

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { InjectionToken, OnDestroy } from '@angular/core';
import { InjectionToken, OnDestroy, ChangeDetectorRef } from '@angular/core';
import { NgGridItemConfig } from 'angular2-grid';
import { Action } from 'vs/base/common/actions';
import { Disposable } from 'vs/base/common/lifecycle';
@@ -63,6 +63,14 @@ export interface TabSettingConfig {
export abstract class DashboardWidget extends Disposable implements OnDestroy {
protected _config: WidgetConfig;
protected _loading: boolean;
protected _inited: boolean = false;
protected _loadingMessage: string;
protected _loadingCompletedMessage: string;
constructor(protected _changeRef: ChangeDetectorRef) {
super();
}
get actions(): Array<Action> {
return [];
@@ -71,4 +79,11 @@ export abstract class DashboardWidget extends Disposable implements OnDestroy {
ngOnDestroy() {
this.dispose();
}
protected setLoadingStatus(loading: boolean): void {
this._loading = loading;
if (this._inited) {
this._changeRef.detectChanges();
}
}
}

View File

@@ -82,7 +82,6 @@ const pageComponents = [ServerDashboardPage, DatabaseDashboardPage];
/* Widget Components */
import { PropertiesWidgetComponent } from 'sql/workbench/contrib/dashboard/browser/widgets/properties/propertiesWidget.component';
import { ExplorerWidget } from 'sql/workbench/contrib/dashboard/browser/widgets/explorer/explorerWidget.component';
import { TasksWidget } from 'sql/workbench/contrib/dashboard/browser/widgets/tasks/tasksWidget.component';
import { InsightsWidget } from 'sql/workbench/contrib/dashboard/browser/widgets/insights/insightsWidget.component';
import { WebviewWidget } from 'sql/workbench/contrib/dashboard/browser/widgets/webview/webviewWidget.component';
import { JobStepsViewComponent } from 'sql/workbench/contrib/jobManagement/browser/jobStepsView.component';
@@ -93,7 +92,6 @@ import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
const widgetComponents = [
PropertiesWidgetComponent,
ExplorerWidget,
TasksWidget,
InsightsWidget,
WebviewWidget
];

View File

@@ -4,11 +4,12 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-->
<div class="explorer-widget" style="display: flex; flex-flow: column; position: absolute; height:100%; width:100%; padding: 10px; box-sizing: border-box">
<div class="explorer-widget"
style="display: flex; flex-flow: column; position: absolute; height:100%; width:100%; padding: 10px; box-sizing: border-box">
<div #input style="width: 100%"></div>
<div style="flex: 1 1 auto; position: relative">
<loading-spinner [loading]="loading" [loadingMessage]="loadingMessage"
[loadingCompletedMessage]="loadingCompletedMessage"></loading-spinner>
<loading-spinner [loading]="_loading" [loadingMessage]="_loadingMessage"
[loadingCompletedMessage]="_loadingCompletedMessage"></loading-spinner>
<div #table style="position: absolute; height: 100%; width: 100%"></div>
</div>
</div>

View File

@@ -47,11 +47,6 @@ export class ExplorerWidget extends DashboardWidget implements IDashboardWidget,
private _treeDataSource = new ExplorerDataSource();
private _treeFilter = new ExplorerFilter();
private _inited = false;
public loading: boolean = false;
public loadingMessage: string;
public loadingCompletedMessage: string;
@ViewChild('input') private _inputContainer: ElementRef;
@ViewChild('table') private _tableContainer: ElementRef;
@@ -64,11 +59,11 @@ export class ExplorerWidget extends DashboardWidget implements IDashboardWidget,
@Inject(IContextViewService) private readonly contextViewService: IContextViewService,
@Inject(IInstantiationService) private readonly instantiationService: IInstantiationService,
@Inject(ICapabilitiesService) private readonly capabilitiesService: ICapabilitiesService,
@Inject(forwardRef(() => ChangeDetectorRef)) private readonly _cd: ChangeDetectorRef
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef
) {
super();
this.loadingMessage = this._config.context === 'database' ? nls.localize('loadingObjects', "loading objects") : nls.localize('loadingDatabases', "loading databases");
this.loadingCompletedMessage = this._config.context === 'database' ? nls.localize('loadingObjectsCompleted', "loading objects completed.") : nls.localize('loadingDatabasesCompleted', "loading databases completed.");
super(changeRef);
this._loadingMessage = this._config.context === 'database' ? nls.localize('loadingObjects', "loading objects") : nls.localize('loadingDatabases', "loading databases");
this._loadingCompletedMessage = this._config.context === 'database' ? nls.localize('loadingObjectsCompleted', "loading objects completed.") : nls.localize('loadingDatabasesCompleted', "loading databases completed.");
this.init();
}
@@ -167,14 +162,6 @@ export class ExplorerWidget extends DashboardWidget implements IDashboardWidget,
}
}
private setLoadingStatus(loading: boolean): void {
this.loading = loading;
if (this._inited) {
this._cd.detectChanges();
}
}
private showErrorMessage(message: string): void {
(<HTMLElement>this._el.nativeElement).innerText = message;
alert(message);

View File

@@ -49,8 +49,8 @@ interface IStorageResult {
<div *ngIf="lastUpdated" style="font-style: italic; font-size: 80%; margin-left: 5px">{{lastUpdated}}</div>
<div *ngIf="autoRefreshStatus" style="font-style: italic; font-size: 80%; margin-left: 5px">{{autoRefreshStatus}}</div>
<div style="margin: 10px; width: calc(100% - 20px); height: calc(100% - 20px)">
<ng-template component-host></ng-template>
<loading-spinner [loading]="_loading"></loading-spinner>
<ng-template *ngIf="!_loading" component-host></ng-template>
<loading-spinner [loading]="_loading" [loadingMessage]="_loadingMessage" [loadingCompletedMessage]="_loadingCompletedMessage"></loading-spinner>
</div>`,
styles: [':host { width: 100%; height: 100% }']
})
@@ -60,8 +60,6 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget,
@ViewChild(ComponentHostDirective) private componentHost: ComponentHostDirective;
private _typeKey: string;
private _init: boolean = false;
public _loading: boolean = true;
private _intervalTimer: IntervalTimer;
public error: string;
@@ -72,16 +70,17 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget,
@Inject(forwardRef(() => ComponentFactoryResolver)) private _componentFactoryResolver: ComponentFactoryResolver,
@Inject(forwardRef(() => CommonServiceInterface)) private dashboardService: CommonServiceInterface,
@Inject(WIDGET_CONFIG) protected _config: WidgetConfig,
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
@Inject(forwardRef(() => Injector)) private _injector: Injector,
@Inject(IInstantiationService) private instantiationService: IInstantiationService,
@Inject(IStorageService) private storageService: IStorageService,
@Inject(IConfigurationService) private readonly _configurationService: IConfigurationService,
@Inject(IFileService) private readonly fileService: IFileService
) {
super();
super(changeRef);
this.insightConfig = <IInsightsConfig>this._config.widget['insights-widget'];
this._loadingMessage = nls.localize('insightsWidgetLoadingMessage', "Loading {0}", this._config.name);
this._loadingCompletedMessage = nls.localize('insightsWidgetLoadingCompletedMessage', "Loading {0} completed", this._config.name);
this._verifyConfig();
this._parseConfig().then(() => {
@@ -91,8 +90,7 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget,
const cancelablePromise = createCancelablePromise(() => {
return promise.then(
result => {
this._loading = false;
if (this._init) {
if (this._inited) {
this._updateChild(result);
this.setupInterval();
} else {
@@ -100,37 +98,36 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget,
}
},
error => {
this._loading = false;
if (isPromiseCanceledError(error)) {
return;
}
if (this._init) {
if (this._inited) {
this.showError(error);
} else {
this.queryObv = Observable.fromPromise(Promise.resolve<SimpleExecuteResult>(error));
}
}
).then(() => this._cd.detectChanges());
).then(() => this._changeRef.detectChanges());
});
this._register(toDisposable(() => cancelablePromise.cancel()));
}
}, error => {
this._loading = false;
this.setLoadingStatus(false);
this.showError(error);
});
}
ngAfterContentInit() {
this._init = true;
this._inited = true;
if (this.queryObv) {
this._register(subscriptionToDisposable(this.queryObv.subscribe(
result => {
this._loading = false;
this.setLoadingStatus(false);
this._updateChild(result);
this.setupInterval();
},
error => {
this._loading = false;
this.setLoadingStatus(false);
this.showError(error);
}
)));
@@ -156,13 +153,13 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget,
let newState = autoRefreshOn ? '' : nls.localize('insights.autoRefreshOffState', "Auto Refresh: OFF");
if (this.autoRefreshStatus !== newState) {
this.autoRefreshStatus = newState;
this._cd.detectChanges();
this._changeRef.detectChanges();
}
}
private showError(error: string): void {
this.error = error;
this._cd.detectChanges();
this._changeRef.detectChanges();
}
get actions(): Array<Action> {
@@ -189,7 +186,7 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget,
results: result
};
this.lastUpdated = nls.localize('insights.lastUpdated', "Last Updated: {0} {1}", currentTime.toLocaleTimeString(), currentTime.toLocaleDateString());
this._cd.detectChanges();
this._changeRef.detectChanges();
this.storageService.store(this._getStorageKey(), JSON.stringify(store), StorageScope.GLOBAL);
}
return result;
@@ -202,11 +199,10 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget,
const storedResult: IStorageResult = JSON.parse(storage);
const date = new Date(storedResult.date);
this.lastUpdated = nls.localize('insights.lastUpdated', "Last Updated: {0} {1}", date.toLocaleTimeString(), date.toLocaleDateString());
this._loading = false;
if (this._init) {
if (this._inited) {
this._updateChild(storedResult.results);
this.setupInterval();
this._cd.detectChanges();
this._changeRef.detectChanges();
} else {
this.queryObv = Observable.fromPromise(Promise.resolve<SimpleExecuteResult>(JSON.parse(storage)));
}
@@ -231,11 +227,14 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget,
}
private _runQuery(): Promise<SimpleExecuteResult> {
this.setLoadingStatus(true);
return Promise.resolve(this.dashboardService.queryManagementService.runQueryAndReturn(this.insightConfig.query as string).then(
result => {
this.setLoadingStatus(false);
return this._storeResult(result);
},
error => {
this.setLoadingStatus(false);
throw error;
}
));
@@ -244,7 +243,7 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget,
private _updateChild(result: SimpleExecuteResult): void {
this.componentHost.viewContainerRef.clear();
this.error = undefined;
this._cd.detectChanges();
this._changeRef.detectChanges();
if (result.rowCount === 0) {
this.showError(nls.localize('noResults', "No results to show"));

View File

@@ -4,7 +4,9 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-->
<div #parent style="position: absolute; height: 100%; width: 100%;">
<loading-spinner [loading]="_loading" [loadingMessage]="_loadingMessage"
[loadingCompletedMessage]="_loadingCompletedMessage"></loading-spinner>
<div #parent style="position: absolute; height: 100%; width: 100%;" [style.display]="_loading ? 'none':'block'">
<div #container [style.margin-right.px]="_clipped ? 30 : 0" [style.width]="_clipped ? 94 + '%' : '100%'" style="overflow: hidden; padding-bottom: 10px">
<span #child style="white-space : nowrap; width: fit-content">
<ng-template ngFor let-item [ngForOf]="properties">

View File

@@ -68,7 +68,6 @@ export class PropertiesWidgetComponent extends DashboardWidget implements IDashb
private _databaseInfo: DatabaseInfo;
public _clipped: boolean;
private properties: Array<DisplayProperty>;
private _hasInit = false;
@ViewChild('child', { read: ElementRef }) private _child: ElementRef;
@ViewChild('parent', { read: ElementRef }) private _parent: ElementRef;
@@ -76,18 +75,20 @@ export class PropertiesWidgetComponent extends DashboardWidget implements IDashb
constructor(
@Inject(forwardRef(() => CommonServiceInterface)) private _bootstrap: CommonServiceInterface,
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
@Inject(WIDGET_CONFIG) protected _config: WidgetConfig,
@Inject(ILogService) private logService: ILogService,
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService
) {
super();
super(changeRef);
this._loadingMessage = nls.localize('loadingProperties', "Loading properties");
this._loadingCompletedMessage = nls.localize('loadingPropertiesCompleted', "Loading properties completed");
this.init();
}
ngOnInit() {
this._hasInit = true;
this._inited = true;
this._register(addDisposableListener(window, EventType.RESIZE, () => this.handleClipping()));
this._changeRef.detectChanges();
@@ -106,14 +107,17 @@ export class PropertiesWidgetComponent extends DashboardWidget implements IDashb
private init(): void {
this._connection = this._bootstrap.connectionManagementService.connectionInfo;
this.setLoadingStatus(true);
this._register(subscriptionToDisposable(this._bootstrap.adminService.databaseInfo.subscribe(data => {
this._databaseInfo = data;
this._changeRef.detectChanges();
this.parseProperties();
if (this._hasInit) {
if (this._inited) {
this.handleClipping();
}
this.setLoadingStatus(false);
}, error => {
this.setLoadingStatus(false);
(<HTMLElement>this._el.nativeElement).innerText = nls.localize('dashboard.properties.error', "Unable to load dashboard properties");
})));
}
@@ -238,7 +242,7 @@ export class PropertiesWidgetComponent extends DashboardWidget implements IDashb
this.properties.push(<DisplayProperty>assignProperty);
}
if (this._hasInit) {
if (this._inited) {
this._changeRef.detectChanges();
}
}

View File

@@ -1,32 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
tasks-widget .tile-container {
position: relative;
display: flex;
flex-flow: row wrap;
align-content: flex-start;
}
tasks-widget .task-tile {
cursor: pointer;
display: flex;
flex-flow: row;
align-items: center;
text-align: center;
margin-top: 10px;
margin-left: 18px;
}
tasks-widget .task-tile > div {
flex: 1 1 auto;
display: flex;
flex-flow: column;
align-items: center;
}
tasks-widget .task-tile .codicon {
padding: 15px;
}

View File

@@ -1,8 +0,0 @@
<!--
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-->
<div #container style="position: absolute; height: 100%; width: 100%">
</div>

View File

@@ -1,178 +0,0 @@
/*---------------------------------------------------------------------------------------------
* 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!sql/media/icons/common-icons';
import 'vs/css!./media/taskWidget';
/* Node Modules */
import { Component, Inject, forwardRef, ViewChild, OnInit, ElementRef } from '@angular/core';
/* SQL imports */
import { DashboardWidget, IDashboardWidget, WidgetConfig, WIDGET_CONFIG } from 'sql/workbench/contrib/dashboard/browser/core/dashboardWidget';
import { CommonServiceInterface } from 'sql/workbench/services/bootstrap/browser/commonServiceInterface.service';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
/* VS imports */
import * as themeColors from 'vs/workbench/common/theme';
import * as colors from 'vs/platform/theme/common/colorRegistry';
import { registerThemingParticipant, ICssStyleCollector, IColorTheme } from 'vs/platform/theme/common/themeService';
import * as types from 'vs/base/common/types';
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import * as DOM from 'vs/base/browser/dom';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { MenuRegistry, ICommandAction } from 'vs/platform/actions/common/actions';
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { TaskRegistry } from 'sql/workbench/services/tasks/browser/tasksRegistry';
interface ITask {
name: string;
when: string;
}
const selector = 'tasks-widget';
@Component({
selector,
templateUrl: decodeURI(require.toUrl('./tasksWidget.component.html'))
})
export class TasksWidget extends DashboardWidget implements IDashboardWidget, OnInit {
private _size: number = 98;
private _tasks: Array<ICommandAction> = [];
private _profile: IConnectionProfile;
private _scrollableElement: ScrollableElement;
private _tileContainer: HTMLElement;
static readonly ICON_PATH_TO_CSS_RULES: Map<string /* path*/, string /* CSS rule */> = new Map<string, string>();
private _inited = false;
@ViewChild('container', { read: ElementRef }) private _container: ElementRef;
constructor(
@Inject(WIDGET_CONFIG) protected _config: WidgetConfig,
@Inject(forwardRef(() => CommonServiceInterface)) private readonly _bootstrap: CommonServiceInterface,
@Inject(ICommandService) private readonly commandService: ICommandService,
@Inject(IContextKeyService) readonly contextKeyService: IContextKeyService
) {
super();
this._profile = this._bootstrap.connectionManagementService.connectionInfo.connectionProfile;
const tasksConfig = this._config.widget[selector] as Array<string | ITask>;
let tasks = TaskRegistry.getTasks();
if (types.isArray(tasksConfig) && tasksConfig.length > 0) {
tasks = tasksConfig.map(i => {
if (types.isString(i)) {
if (tasks.some(x => x === i)) {
return i;
}
} else {
if (tasks.some(x => x === i.name) && contextKeyService.contextMatchesRules(ContextKeyExpr.deserialize(i.when))) {
return i.name;
}
}
return undefined;
}).filter(i => !!i);
}
this._tasks = tasks.map(i => MenuRegistry.getCommand(i)).filter(v => !!v);
}
ngOnInit() {
this._inited = true;
this._register(registerThemingParticipant(this.registerThemeing));
this._computeContainer();
this._tasks.map(a => {
this._tileContainer.append(this._createTile(a));
});
this._scrollableElement = this._register(new ScrollableElement(this._tileContainer, {
horizontal: ScrollbarVisibility.Auto,
vertical: ScrollbarVisibility.Hidden,
scrollYToX: true,
useShadows: false
}));
this._scrollableElement.onScroll(e => {
this._tileContainer.style.right = e.scrollLeft + 'px';
});
(this._container.nativeElement as HTMLElement).appendChild(this._scrollableElement.getDomNode());
// Update scrollbar
this._scrollableElement.setScrollDimensions({
width: DOM.getContentWidth(this._container.nativeElement),
scrollWidth: DOM.getContentWidth(this._tileContainer) + 18 // right padding
});
}
private _computeContainer(): void {
const height = DOM.getContentHeight(this._container.nativeElement);
const tilesHeight = Math.floor(height / (this._size + 10));
const width = (this._size + 18) * Math.ceil(this._tasks.length / tilesHeight);
if (!this._tileContainer) {
this._tileContainer = DOM.$('.tile-container');
}
this._tileContainer.style.height = height + 'px';
this._tileContainer.style.width = width + 'px';
}
private _createTile(action: ICommandAction): HTMLElement {
const label = DOM.$('div');
label.innerText = types.isString(action.title) ? action.title : action.title.value;
const tile = DOM.$('.task-tile');
tile.style.height = this._size + 'px';
tile.style.width = this._size + 'px';
const innerTile = DOM.$('div');
const iconClassName = TaskRegistry.getOrCreateTaskIconClassName(action);
if (iconClassName) {
const icon = DOM.$('span.codicon');
DOM.addClass(icon, iconClassName);
innerTile.append(icon);
}
innerTile.append(label);
tile.append(innerTile);
tile.setAttribute('tabindex', '0');
this._register(DOM.addDisposableListener(tile, DOM.EventType.CLICK, () => this.runTask(action)));
this._register(DOM.addDisposableListener(tile, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => {
const event = new StandardKeyboardEvent(e);
if (event.equals(KeyCode.Enter)) {
this.runTask(action);
e.stopImmediatePropagation();
}
}));
return tile;
}
private registerThemeing(theme: IColorTheme, collector: ICssStyleCollector) {
const contrastBorder = theme.getColor(colors.contrastBorder);
const sideBarColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND);
if (contrastBorder) {
const contrastBorderString = contrastBorder.toString();
collector.addRule(`tasks-widget .task-tile { border: 1px solid ${contrastBorderString} }`);
} else {
const sideBarColorString = sideBarColor.toString();
collector.addRule(`tasks-widget .task-tile { background-color: ${sideBarColorString} }`);
}
}
public runTask(task: ICommandAction) {
this.commandService.executeCommand(task.id, this._profile);
}
public layout(): void {
if (this._inited) {
this._computeContainer();
// Update scrollbar
this._scrollableElement.setScrollDimensions({
width: DOM.getContentWidth(this._container.nativeElement),
scrollWidth: DOM.getContentWidth(this._tileContainer) + 18 // right padding
});
}
}
}

View File

@@ -1,38 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { registerDashboardWidget } from 'sql/platform/dashboard/browser/widgetRegistry';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { TaskRegistry } from 'sql/workbench/services/tasks/browser/tasksRegistry';
const singleTaskSchema: IJSONSchema = {
type: 'string',
enum: TaskRegistry.getTasks()
};
const tasksSchema: IJSONSchema = {
type: 'array',
items: {
anyOf: [
singleTaskSchema,
{
type: 'object',
properties: {
name: singleTaskSchema,
when: {
type: 'string'
}
}
}
]
}
};
TaskRegistry.onTaskRegistered(e => {
singleTaskSchema.enum.push(e);
});
registerDashboardWidget('tasks-widget', '', tasksSchema);

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Component, Inject, forwardRef, OnInit, ElementRef } from '@angular/core';
import { Component, Inject, forwardRef, OnInit, ElementRef, ChangeDetectorRef } from '@angular/core';
import { Event, Emitter } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
@@ -41,9 +41,10 @@ export class WebviewWidget extends DashboardWidget implements IDashboardWidget,
@Inject(WIDGET_CONFIG) protected readonly _config: WidgetConfig,
@Inject(forwardRef(() => ElementRef)) private readonly _el: ElementRef,
@Inject(IDashboardViewService) private readonly dashboardViewService: IDashboardViewService,
@Inject(IWebviewService) private readonly webviewService: IWebviewService
@Inject(IWebviewService) private readonly webviewService: IWebviewService,
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef
) {
super();
super(changeRef);
this._id = (_config.widget[selector] as IWebviewWidgetConfig).id;
}

View File

@@ -435,7 +435,6 @@ import 'sql/workbench/contrib/dashboard/browser/widgets/insights/views/tableInsi
import 'sql/workbench/contrib/dashboard/browser/dashboard.contribution';
import 'sql/workbench/contrib/dashboard/browser/widgets/insights/insightsWidget.contribution';
import 'sql/workbench/contrib/dashboard/browser/widgets/explorer/explorerWidget.contribution';
import 'sql/workbench/contrib/dashboard/browser/widgets/tasks/tasksWidget.contribution';
import 'sql/workbench/contrib/dashboard/browser/widgets/webview/webviewWidget.contribution';
import 'sql/workbench/contrib/dashboard/browser/containers/dashboardWebviewContainer.contribution';
import 'sql/workbench/contrib/dashboard/browser/containers/dashboardControlHostContainer.contribution';