Refresh master with initial release/0.24 snapshot (#332)

* Initial port of release/0.24 source code

* Fix additional headers

* Fix a typo in launch.json
This commit is contained in:
Karl Burtram
2017-12-15 15:38:57 -08:00
committed by GitHub
parent 271b3a0b82
commit 6ad0df0e3e
7118 changed files with 107999 additions and 56466 deletions

View File

@@ -2,28 +2,112 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Action } from 'vs/base/common/actions';
import { Action, IAction } from 'vs/base/common/actions';
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IAngularEventingService, AngularEventType } from 'sql/services/angularEventing/angularEventingService';
export class RefreshWidgetAction extends Action {
export class EditDashboardAction extends Action {
public static ID = 'refreshWidget';
public static LABEL = nls.localize('refreshWidget', 'Refresh');
private static readonly ID = 'editDashboard';
private static readonly EDITLABEL = nls.localize('editDashboard', "Edit");
private static readonly EXITLABEL = nls.localize('editDashboardExit', "Exit");
private static readonly ICON = 'edit';
private _state = 0;
constructor(
id: string, label: string,
private refreshFn: () => void
private editFn: () => void,
private context: any //this
) {
super(id, label);
super(EditDashboardAction.ID, EditDashboardAction.EDITLABEL, EditDashboardAction.ICON);
}
run(): TPromise<boolean> {
try {
this.refreshFn();
this.editFn.apply(this.context);
this.toggleLabel();
return TPromise.as(true);
} catch (e) {
return TPromise.as(false);
}
}
private toggleLabel(): void {
if (this._state === 0) {
this.label = EditDashboardAction.EXITLABEL;
this._state = 1;
} else {
this.label = EditDashboardAction.EDITLABEL;
this._state = 0;
}
}
}
export class RefreshWidgetAction extends Action {
private static readonly ID = 'refreshWidget';
private static readonly LABEL = nls.localize('refreshWidget', 'Refresh');
private static readonly ICON = 'refresh';
constructor(
private refreshFn: () => void,
private context: any // this
) {
super(RefreshWidgetAction.ID, RefreshWidgetAction.LABEL, RefreshWidgetAction.ICON);
}
run(): TPromise<boolean> {
try {
this.refreshFn.apply(this.context);
return TPromise.as(true);
} catch (e) {
return TPromise.as(false);
}
}
}
export class ToggleMoreWidgetAction extends Action {
private static readonly ID = 'toggleMore';
private static readonly LABEL = nls.localize('toggleMore', 'Toggle More');
private static readonly ICON = 'toggle-more';
constructor(
private _actions: Array<IAction>,
private _context: any,
@IContextMenuService private _contextMenuService: IContextMenuService
) {
super(ToggleMoreWidgetAction.ID, ToggleMoreWidgetAction.LABEL, ToggleMoreWidgetAction.ICON);
}
run(context: StandardKeyboardEvent): TPromise<boolean> {
this._contextMenuService.showContextMenu({
getAnchor: () => context.target,
getActions: () => TPromise.as(this._actions),
getActionsContext: () => this._context
});
return TPromise.as(true);
}
}
export class DeleteWidgetAction extends Action {
private static readonly ID = 'deleteWidget';
private static readonly LABEL = nls.localize('deleteWidget', "Delete Widget");
private static readonly ICON = 'close';
constructor(
private _widgetId,
private _uri,
@IAngularEventingService private angularEventService: IAngularEventingService
) {
super(DeleteWidgetAction.ID, DeleteWidgetAction.LABEL, DeleteWidgetAction.ICON);
}
run(): TPromise<boolean> {
this.angularEventService.sendAngularEvent(this._uri, AngularEventType.DELETE_WIDGET, { id: this._widgetId });
return TPromise.as(true);
}
}

View File

@@ -4,13 +4,15 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-->
<div #propertyContainer>
<dashboard-widget-wrapper #properties *ngIf="propertiesWidget" [_config]="propertiesWidget" style="margin-left: 10px; margin-right: 10px; height: 90px; display: block">
</dashboard-widget-wrapper>
</div>
<div>
<div [ngGrid]="gridConfig">
<dashboard-widget-wrapper *ngFor="let widget of widgets" [(ngGridItem)]="widget.gridItemConfig" [_config]="widget">
</dashboard-widget-wrapper>
<div #scrollContainer style="height: 100%">
<div #scrollable style="position: relative">
<div #propertiesContainer>
<dashboard-widget-wrapper #properties *ngIf="propertiesWidget" [_config]="propertiesWidget" style="padding-left: 10px; padding-right: 10px; height: 90px; display: block">
</dashboard-widget-wrapper>
</div>
<div [ngGrid]="gridConfig">
<dashboard-widget-wrapper *ngFor="let widget of widgets" [(ngGridItem)]="widget.gridItemConfig" [_config]="widget">
</dashboard-widget-wrapper>
</div>
</div>
</div>
</div>

View File

@@ -3,22 +3,33 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Component, Inject, forwardRef, ViewChild, ElementRef, ViewChildren, QueryList } from '@angular/core';
import { NgGridConfig } from 'angular2-grid';
import 'vs/css!./dashboardPage';
import { Component, Inject, forwardRef, ViewChild, ElementRef, ViewChildren, QueryList, OnDestroy, ChangeDetectorRef } from '@angular/core';
import { NgGridConfig, NgGrid, NgGridItem } from 'angular2-grid';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo';
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/common/dashboardWidgetWrapper.component';
import { subscriptionToDisposable } from 'sql/base/common/lifecycle';
import { IPropertiesConfig } from 'sql/parts/dashboard/pages/serverDashboardPage.contribution';
import { Registry } from 'vs/platform/registry/common/platform';
import * as types from 'vs/base/common/types';
import { Severity } from 'vs/platform/message/common/message';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import * as nls from 'vs/nls';
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { addDisposableListener, getContentHeight, EventType } from 'vs/base/browser/dom';
import { IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
import * as colors from 'vs/platform/theme/common/colorRegistry';
import * as nls from 'vs/nls';
import * as themeColors from 'vs/workbench/common/theme';
import { generateUuid } from 'vs/base/common/uuid';
import * as objects from 'vs/base/common/objects';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
/**
* @returns whether the provided parameter is a JavaScript Array and each element in the array is a number.
@@ -27,14 +38,56 @@ function isNumberArray(value: any): value is number[] {
return types.isArray(value) && (<any[]>value).every(elem => types.isNumber(elem));
}
/**
* Sorting function for dashboard widgets
* In order of priority;
* If neither have defined grid positions, they are equivalent
* If a has a defined grid position and b does not; a should come first
* If both have defined grid positions and have the same row; the one with the smaller col position should come first
* If both have defined grid positions but different rows (it doesn't really matter in this case) the lowers row should come first
*/
function configSorter(a, b): number {
if ((!a.gridItemConfig || !a.gridItemConfig.col)
&& (!b.gridItemConfig || !b.gridItemConfig.col)) {
return 0;
} else if (!a.gridItemConfig || !a.gridItemConfig.col) {
return 1;
} else if (!b.gridItemConfig || !b.gridItemConfig.col) {
return -1;
} else if (a.gridItemConfig.row === b.gridItemConfig.row) {
if (a.gridItemConfig.col < b.gridItemConfig.col) {
return -1;
}
if (a.gridItemConfig.col === b.gridItemConfig.col) {
return 0;
}
if (a.gridItemConfig.col > b.gridItemConfig.col) {
return 1;
}
} else {
if (a.gridItemConfig.row < b.gridItemConfig.row) {
return -1;
}
if (a.gridItemConfig.row === b.gridItemConfig.row) {
return 0;
}
if (a.gridItemConfig.row > b.gridItemConfig.row) {
return 1;
}
}
return void 0; // this should never be reached
}
@Component({
selector: 'dashboard-page',
templateUrl: decodeURI(require.toUrl('sql/parts/dashboard/common/dashboardPage.component.html')),
host: {
class: 'dashboard-page'
}
templateUrl: decodeURI(require.toUrl('sql/parts/dashboard/common/dashboardPage.component.html'))
})
export abstract class DashboardPage {
export abstract class DashboardPage extends Disposable implements OnDestroy {
protected SKELETON_WIDTH = 5;
protected widgets: Array<WidgetConfig> = [];
@@ -60,55 +113,124 @@ export abstract class DashboardPage {
'prefer_new': false, // When adding new items, will use that items position ahead of existing items
'limit_to_screen': true, // When resizing the screen, with this true and auto_resize false, the grid will re-arrange to fit the screen size. Please note, at present this only works with cascade direction up.
};
private _themeDispose: IDisposable;
private _originalConfig: WidgetConfig[];
private _editDispose: Array<IDisposable> = [];
private _scrollableElement: ScrollableElement;
private _widgetConfigLocation: string;
private _propertiesConfigLocation: string;
@ViewChild('propertyContainer', { read: ElementRef }) private propertyContainer: ElementRef;
@ViewChild('properties') private _properties: DashboardWidgetWrapper;
@ViewChild(NgGrid) private _grid: NgGrid;
@ViewChild('scrollable', { read: ElementRef }) private _scrollable: ElementRef;
@ViewChild('scrollContainer', { read: ElementRef }) private _scrollContainer: ElementRef;
@ViewChild('propertiesContainer', { read: ElementRef }) private _propertiesContainer: ElementRef;
@ViewChildren(DashboardWidgetWrapper) private _widgets: QueryList<DashboardWidgetWrapper>;
@ViewChildren(NgGridItem) private _items: QueryList<NgGridItem>;
// a set of config modifiers
private readonly _configModifiers: Array<(item: Array<WidgetConfig>) => Array<WidgetConfig>> = [
this.removeEmpty,
this.initExtensionConfigs,
this.validateGridConfig,
this.addProvider,
this.addEdition,
this.addContext,
this.filterWidgets
];
private readonly _gridModifiers: Array<(item: Array<WidgetConfig>) => Array<WidgetConfig>> = [
this.validateGridConfig
];
constructor(
@Inject(forwardRef(() => DashboardServiceInterface)) protected dashboardService: DashboardServiceInterface
) { }
@Inject(forwardRef(() => DashboardServiceInterface)) protected dashboardService: DashboardServiceInterface,
@Inject(forwardRef(() => ElementRef)) protected _el: ElementRef,
@Inject(forwardRef(() => ChangeDetectorRef)) protected _cd: ChangeDetectorRef
) {
super();
}
protected init() {
if (!this.dashboardService.connectionManagementService.connectionInfo) {
this.dashboardService.messageService.show(Severity.Warning, nls.localize('missingConnectionInfo', 'No connection information could be found for this dashboard'));
} else {
let tempWidgets = this.dashboardService.getSettings(this.context).widgets;
let tempWidgets = this.dashboardService.getSettings<Array<WidgetConfig>>([this.context, 'widgets'].join('.'));
this._widgetConfigLocation = 'default';
this._originalConfig = objects.clone(tempWidgets);
let properties = this.getProperties();
this._configModifiers.forEach((cb) => {
tempWidgets = cb.apply(this, [tempWidgets]);
properties = properties ? cb.apply(this, [properties]) : undefined;
});
this._gridModifiers.forEach(cb => {
tempWidgets = cb.apply(this, [tempWidgets]);
});
this.widgets = tempWidgets;
this.propertiesWidget = properties ? properties[0] : undefined;
}
}
protected baseInit(): void {
let self = this;
self._themeDispose = self.dashboardService.themeService.onDidColorThemeChange((event: IColorTheme) => {
self.updateTheme(event);
ngAfterViewInit(): void {
this._register(this.dashboardService.themeService.onDidColorThemeChange(this.updateTheme, this));
this.updateTheme(this.dashboardService.themeService.getColorTheme());
let container = this._scrollContainer.nativeElement as HTMLElement;
let scrollable = this._scrollable.nativeElement as HTMLElement;
container.removeChild(scrollable);
this._scrollableElement = new ScrollableElement(scrollable, {
horizontal: ScrollbarVisibility.Hidden,
vertical: ScrollbarVisibility.Auto,
useShadows: false
});
self.updateTheme(self.dashboardService.themeService.getColorTheme());
this._scrollableElement.onScroll(e => {
scrollable.style.bottom = e.scrollTop + 'px';
});
container.appendChild(this._scrollableElement.getDomNode());
let initalHeight = getContentHeight(scrollable);
this._scrollableElement.setScrollDimensions({
scrollHeight: getContentHeight(scrollable),
height: getContentHeight(container)
});
this._register(addDisposableListener(window, EventType.RESIZE, () => {
this._scrollableElement.setScrollDimensions({
scrollHeight: getContentHeight(scrollable),
height: getContentHeight(container)
});
}));
// unforunately because of angular rendering behavior we need to do a double check to make sure nothing changed after this point
setTimeout(() => {
let currentheight = getContentHeight(scrollable);
if (initalHeight !== currentheight) {
this._scrollableElement.setScrollDimensions({
scrollHeight: getContentHeight(scrollable),
height: getContentHeight(container)
});
}
}, 100);
}
private updateTheme(theme: IColorTheme): void {
let el = this._propertiesContainer.nativeElement as HTMLElement;
let border = theme.getColor(colors.contrastBorder, true);
let borderColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true);
if (border) {
el.style.borderColor = border.toString();
el.style.borderBottomWidth = '1px';
el.style.borderBottomStyle = 'solid';
} else if (borderColor) {
el.style.borderBottom = '1px solid ' + borderColor.toString();
} else {
el.style.border = 'none';
}
}
protected baseDestroy(): void {
if (this._themeDispose) {
this._themeDispose.dispose();
}
ngOnDestroy() {
this.dispose();
}
protected abstract propertiesWidget: WidgetConfig;
@@ -215,10 +337,14 @@ export abstract class DashboardPage {
* @param config Array of widgets to validate
*/
protected validateGridConfig(config: WidgetConfig[]): Array<WidgetConfig> {
return config.map((widget) => {
return config.map((widget, index) => {
if (widget.gridItemConfig === undefined) {
widget.gridItemConfig = {};
}
const id = generateUuid();
widget.gridItemConfig.payload = { id };
widget.id = id;
this._originalConfig[index].id = id;
return widget;
});
}
@@ -246,6 +372,12 @@ export abstract class DashboardPage {
sizey: insightConfig.gridItemConfig.y
};
}
if (config.gridItemConfig && !config.gridItemConfig.sizex && insightConfig.gridItemConfig && insightConfig.gridItemConfig.x) {
config.gridItemConfig.sizex = insightConfig.gridItemConfig.x;
}
if (config.gridItemConfig && !config.gridItemConfig.sizey && insightConfig.gridItemConfig && insightConfig.gridItemConfig.y) {
config.gridItemConfig.sizey = insightConfig.gridItemConfig.y;
}
}
}
return config;
@@ -253,7 +385,8 @@ export abstract class DashboardPage {
}
private getProperties(): Array<WidgetConfig> {
let properties = this.dashboardService.getSettings(this.context).properties;
let properties = this.dashboardService.getSettings<IPropertiesConfig[]>([this.context, 'properties'].join('.'));
this._propertiesConfigLocation = 'default';
if (types.isUndefinedOrNull(properties)) {
return [this.propertiesWidget];
} else if (types.isBoolean(properties)) {
@@ -271,18 +404,6 @@ export abstract class DashboardPage {
}
}
private updateTheme(theme: IColorTheme): void {
let propsEl: HTMLElement = this.propertyContainer.nativeElement;
let widgetShadowColor = theme.getColor(colors.widgetShadow);
if (widgetShadowColor) {
// Box shadow on bottom only.
// The below settings fill the shadow across the whole page
propsEl.style.boxShadow = `-5px 5px 10px -5px ${widgetShadowColor}`;
propsEl.style.marginRight = '-10px';
propsEl.style.marginBottom = '5px';
}
}
public refresh(refreshConfig: boolean = false): void {
if (refreshConfig) {
this.init();
@@ -297,4 +418,85 @@ export abstract class DashboardPage {
}
}
}
public enableEdit(): void {
if (this._grid.dragEnable) {
this._grid.disableDrag();
this._grid.disableResize();
this._editDispose.forEach(i => i.dispose());
this._widgets.forEach(i => {
if (i.id) {
i.disableEdit();
}
});
this._editDispose = [];
} else {
this._grid.enableResize();
this._grid.enableDrag();
this._editDispose.push(this.dashboardService.onDeleteWidget(e => {
let index = this.widgets.findIndex(i => i.id === e);
this.widgets.splice(index, 1);
index = this._originalConfig.findIndex(i => i.id === e);
this._originalConfig.splice(index, 1);
this._rewriteConfig();
this._cd.detectChanges();
}));
this._editDispose.push(subscriptionToDisposable(this._grid.onResizeStop.subscribe((e: NgGridItem) => {
this._scrollableElement.setScrollDimensions({
scrollHeight: getContentHeight(this._scrollable.nativeElement),
height: getContentHeight(this._scrollContainer.nativeElement)
});
let event = e.getEventOutput();
let config = this._originalConfig.find(i => i.id === event.payload.id);
if (!config.gridItemConfig) {
config.gridItemConfig = {};
}
config.gridItemConfig.sizex = e.sizex;
config.gridItemConfig.sizey = e.sizey;
let component = this._widgets.find(i => i.id === event.payload.id);
component.layout();
this._rewriteConfig();
})));
this._editDispose.push(subscriptionToDisposable(this._grid.onDragStop.subscribe((e: NgGridItem) => {
this._scrollableElement.setScrollDimensions({
scrollHeight: getContentHeight(this._scrollable.nativeElement),
height: getContentHeight(this._scrollContainer.nativeElement)
});
let event = e.getEventOutput();
this._items.forEach(i => {
let config = this._originalConfig.find(j => j.id === i.getEventOutput().payload.id);
if ((config.gridItemConfig && config.gridItemConfig.col) || config.id === event.payload.id) {
if (!config.gridItemConfig) {
config.gridItemConfig = {};
}
config.gridItemConfig.col = i.col;
config.gridItemConfig.row = i.row;
}
});
this._originalConfig.sort(configSorter);
this._rewriteConfig();
})));
this._widgets.forEach(i => {
if (i.id) {
i.enableEdit();
}
});
}
}
private _rewriteConfig(): void {
let writeableConfig = objects.clone(this._originalConfig);
writeableConfig.forEach(i => {
delete i.id;
});
let target: ConfigurationTarget = ConfigurationTarget.USER;
this.dashboardService.writeSettings(this.context, writeableConfig, target);
}
}

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-page {
height: 100%;
width: 100%;
position: absolute;
}

View File

@@ -2,19 +2,22 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { InjectionToken } from '@angular/core';
import { InjectionToken, OnDestroy } from '@angular/core';
import { NgGridItemConfig } from 'angular2-grid';
import { Action } from 'vs/base/common/actions';
import { Disposable } from 'vs/base/common/lifecycle';
export interface IDashboardWidget {
actions: Array<Action>;
actionsContext?: any;
refresh?: () => void;
layout?: () => void;
}
export const WIDGET_CONFIG = new InjectionToken<WidgetConfig>('widget_config');
export interface WidgetConfig {
id?: string; // used to track the widget lifespan operations
name?: string;
icon?: string;
context: string;
@@ -26,13 +29,17 @@ export interface WidgetConfig {
border?: string;
fontSize?: string;
fontWeight?: string;
padding?:string;
padding?: string;
}
export abstract class DashboardWidget {
export abstract class DashboardWidget extends Disposable implements OnDestroy {
protected _config: WidgetConfig;
get actions(): Array<Action> {
return [];
}
}
ngOnDestroy() {
this.dispose();
}
}

View File

@@ -7,12 +7,12 @@
<div style="display: flex; flex-flow: column; overflow: hidden; height: 100%; width: 100%">
<div #header>
<div *ngIf="_config.name || _config.loadedIcon || _actions" style="display: flex;flex: 0 0; padding: 3px 0 3px 0">
<span *ngIf="_config.icon" [ngClass]="['icon', _config.icon]" style="display: inline-block; padding: 10px; margin-left: 5px"></span>
<div *ngIf="_config.name || _config.loadedIcon || _actions" style="display: flex; flex: 0 0; padding: 3px 0 3px 0; flex-direction: row-reverse; justify-content: space-between">
<span #actionbar style="flex: 0 0 auto; align-self: end"></span>
<span *ngIf="_config.name" style="margin-left: 5px">{{_config.name}}</span>
<div *ngIf="_actions" (click)="onActionsClick($event)" style="float: right; margin-right: 5px; margin-left: auto; padding: 10px" class="icon toggle-more"></div>
<span *ngIf="_config.icon" [ngClass]="['icon', _config.icon]" style="display: inline-block; padding: 10px; margin-left: 5px"></span>
</div>
</div>
<ng-template component-host>
</ng-template>
</div>
</div>

View File

@@ -3,6 +3,7 @@
* 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!./dashboardWidgetWrapper';
import {
Component, Input, Inject, forwardRef, ComponentFactoryResolver, AfterContentInit, ViewChild,
@@ -13,7 +14,7 @@ import { ComponentHostDirective } from './componentHost.directive';
import { WidgetConfig, WIDGET_CONFIG, IDashboardWidget } from './dashboardWidget';
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
import { error } from 'sql/base/common/log';
import * as ACTIONS from './actions';
import { RefreshWidgetAction, ToggleMoreWidgetAction, DeleteWidgetAction } from './actions';
/* Widgets */
import { PropertiesWidgetComponent } from 'sql/parts/dashboard/widgets/properties/propertiesWidget.component';
@@ -28,8 +29,8 @@ import { IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeS
import * as colors from 'vs/platform/theme/common/colorRegistry';
import * as themeColors from 'vs/workbench/common/theme';
import { Action } from 'vs/base/common/actions';
import { TPromise } from 'vs/base/common/winjs.base';
import { Registry } from 'vs/platform/registry/common/platform';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
const componentMap: { [x: string]: Type<IDashboardWidget> } = {
'properties-widget': PropertiesWidgetComponent,
@@ -47,8 +48,10 @@ export class DashboardWidgetWrapper implements AfterContentInit, OnInit, OnDestr
private _themeDispose: IDisposable;
private _actions: Array<Action>;
private _component: IDashboardWidget;
private _actionbar: ActionBar;
@ViewChild('header', { read: ElementRef }) private header: ElementRef;
@ViewChild('actionbar', { read: ElementRef }) private _actionbarRef: ElementRef;
@ViewChild(ComponentHostDirective) componentHost: ComponentHostDirective;
constructor(
@@ -69,6 +72,11 @@ export class DashboardWidgetWrapper implements AfterContentInit, OnInit, OnDestr
ngAfterContentInit() {
this.updateTheme(this._bootstrap.themeService.getColorTheme());
this.loadWidget();
this._changeref.detectChanges();
this._actionbar = new ActionBar(this._actionbarRef.nativeElement);
if (this._actions) {
this._actionbar.push(this._bootstrap.instantiationService.createInstance(ToggleMoreWidgetAction, this._actions, this._component.actionsContext), { icon: true, label: false });
}
}
ngOnDestroy() {
@@ -81,6 +89,24 @@ export class DashboardWidgetWrapper implements AfterContentInit, OnInit, OnDestr
}
}
public layout(): void {
if (this._component && this._component.layout) {
this._component.layout();
}
}
public get id(): string {
return this._config.id;
}
public enableEdit(): void {
this._actionbar.push(this._bootstrap.instantiationService.createInstance(DeleteWidgetAction, this._config.id, this._bootstrap.getUnderlyingUri()), { icon: true, label: false });
}
public disableEdit(): void {
this._actionbar.pull(this._actionbar.length() - 1);
}
private loadWidget(): void {
if (Object.keys(this._config.widget).length !== 1) {
error('Exactly 1 widget must be defined per space');
@@ -105,7 +131,7 @@ export class DashboardWidgetWrapper implements AfterContentInit, OnInit, OnDestr
this._component = componentRef.instance;
let actions = componentRef.instance.actions;
if (componentRef.instance.refresh) {
actions.push(this._bootstrap.instantiationService.createInstance(ACTIONS.RefreshWidgetAction, ACTIONS.RefreshWidgetAction.ID, ACTIONS.RefreshWidgetAction.LABEL, componentRef.instance.refresh));
actions.push(new RefreshWidgetAction(componentRef.instance.refresh, componentRef.instance));
}
if (actions !== undefined && actions.length > 0) {
this._actions = actions;
@@ -148,23 +174,12 @@ export class DashboardWidgetWrapper implements AfterContentInit, OnInit, OnDestr
return selector;
}
//tslint:disable-next-line
private onActionsClick(e: any) {
let anchor = { x: e.pageX + 1, y: e.pageY };
this._bootstrap.contextMenuService.showContextMenu({
getAnchor: () => anchor,
getActions: () => TPromise.as(this._actions),
getActionsContext: () => this._component.actionsContext
});
}
private updateTheme(theme: IColorTheme): void {
let el = <HTMLElement>this._ref.nativeElement;
let headerEl: HTMLElement = this.header.nativeElement;
let borderColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true);
let backgroundColor = theme.getColor(colors.editorBackground, true);
let foregroundColor = theme.getColor(themeColors.SIDE_BAR_FOREGROUND, true);
// TODO: highContrastBorder does not exist, how to handle?
let border = theme.getColor(colors.contrastBorder, true);
if (this._config.background_color) {

View File

@@ -0,0 +1,8 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
dashboard-widget-wrapper .action-label {
padding: 7px;
}