Collapsible properties (#771)

* started moving properties to home tab

* moved properties

* refactored panel in dashboard

* fix errors

* fix miss-naming

* added collapsable properties

* revert unnecessary change

* add icon for collapsing properties
This commit is contained in:
Anthony Dresser
2018-03-05 10:06:18 -08:00
committed by GitHub
parent 2e67d03b56
commit 692ed02df8
14 changed files with 146 additions and 24 deletions

View File

@@ -197,3 +197,36 @@ export class AddFeatureTabAction extends Action {
} }
} }
} }
export class CollapseWidgetAction extends Action {
private static readonly ID = 'collapseWidget';
private static readonly COLLPASE_LABEL = nls.localize('collapseWidget', "Collapse");
private static readonly EXPAND_LABEL = nls.localize('expandWidget', "Expand");
private static readonly COLLAPSE_ICON = 'maximize-panel-action';
private static readonly EXPAND_ICON = 'minimize-panel-action';
constructor(
private _uri: string,
private _widgetUuid: string,
private collpasedState: boolean,
@IAngularEventingService private _angularEventService: IAngularEventingService
) {
super(
CollapseWidgetAction.ID,
collpasedState ? CollapseWidgetAction.EXPAND_LABEL : CollapseWidgetAction.COLLPASE_LABEL,
collpasedState ? CollapseWidgetAction.EXPAND_ICON : CollapseWidgetAction.COLLAPSE_ICON
);
}
run(): TPromise<boolean> {
this._toggleState();
this._angularEventService.sendAngularEvent(this._uri, AngularEventType.COLLAPSE_WIDGET, this._widgetUuid);
return TPromise.as(true);
}
private _toggleState(): void {
this.collpasedState = !this.collpasedState;
this._setClass(this.collpasedState ? CollapseWidgetAction.EXPAND_ICON : CollapseWidgetAction.COLLAPSE_ICON);
this._setLabel(this.collpasedState ? CollapseWidgetAction.EXPAND_LABEL : CollapseWidgetAction.COLLPASE_LABEL);
}
}

View File

@@ -89,7 +89,6 @@ export abstract class DashboardPage extends Disposable implements OnDestroy {
constructor( constructor(
@Inject(forwardRef(() => DashboardServiceInterface)) protected dashboardService: DashboardServiceInterface, @Inject(forwardRef(() => DashboardServiceInterface)) protected dashboardService: DashboardServiceInterface,
@Inject(BOOTSTRAP_SERVICE_ID) protected bootstrapService: IBootstrapService,
@Inject(forwardRef(() => ElementRef)) protected _el: ElementRef, @Inject(forwardRef(() => ElementRef)) protected _el: ElementRef,
@Inject(forwardRef(() => ChangeDetectorRef)) protected _cd: ChangeDetectorRef @Inject(forwardRef(() => ChangeDetectorRef)) protected _cd: ChangeDetectorRef
) { ) {
@@ -252,12 +251,14 @@ export abstract class DashboardPage extends Disposable implements OnDestroy {
} }
private getProperties(): Array<WidgetConfig> { private getProperties(): Array<WidgetConfig> {
let properties = this.dashboardService.getSettings<IPropertiesConfig[]>([this.context, 'properties'].join('.')); let properties = this.dashboardService.getSettings<IPropertiesConfig[] | string | boolean>([this.context, 'properties'].join('.'));
this._propertiesConfigLocation = 'default'; this._propertiesConfigLocation = 'default';
if (types.isUndefinedOrNull(properties)) { if (types.isUndefinedOrNull(properties)) {
return [this.propertiesWidget]; return [this.propertiesWidget];
} else if (types.isBoolean(properties)) { } else if (types.isBoolean(properties)) {
return properties ? [this.propertiesWidget] : []; return properties ? [this.propertiesWidget] : [];
} else if (types.isString(properties) && properties === 'collapsed') {
return [this.propertiesWidget];
} else if (types.isArray(properties)) { } else if (types.isArray(properties)) {
return properties.map((item) => { return properties.map((item) => {
let retVal = Object.assign({}, this.propertiesWidget); let retVal = Object.assign({}, this.propertiesWidget);
@@ -302,6 +303,6 @@ export abstract class DashboardPage extends Disposable implements OnDestroy {
let index = this.tabs.findIndex(i => i.id === tab.identifier); let index = this.tabs.findIndex(i => i.id === tab.identifier);
this.tabs.splice(index, 1); this.tabs.splice(index, 1);
this._cd.detectChanges(); this._cd.detectChanges();
this.bootstrapService.angularEventingService.sendAngularEvent(this.dashboardService.getUnderlyingUri(), AngularEventType.CLOSE_TAB, { id: tab.identifier }); this.dashboardService.angularEventingService.sendAngularEvent(this.dashboardService.getUnderlyingUri(), AngularEventType.CLOSE_TAB, { id: tab.identifier });
} }
} }

View File

@@ -5,11 +5,15 @@
import 'vs/css!./dashboardHomeContainer'; import 'vs/css!./dashboardHomeContainer';
import { Component, forwardRef, Input } from '@angular/core'; import { Component, forwardRef, Input, ChangeDetectorRef, Inject, ViewChild } from '@angular/core';
import { DashboardWidgetContainer } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.component'; import { DashboardWidgetContainer } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.component';
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces'; import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
import { WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget'; import { WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { AngularEventType } from '../../../services/angularEventing/angularEventingService';
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/contents/dashboardWidgetWrapper.component';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
@Component({ @Component({
selector: 'dashboard-home-container', selector: 'dashboard-home-container',
@@ -17,7 +21,8 @@ import { WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
template: ` template: `
<div class="scroll-container" #scrollContainer> <div class="scroll-container" #scrollContainer>
<div class="scrollable" #scrollable> <div class="scrollable" #scrollable>
<dashboard-widget-wrapper *ngIf="properties" [_config]="properties" style="padding-left: 10px; padding-right: 10px; height: 90px; display: block"> <dashboard-widget-wrapper #propertiesClass *ngIf="properties" [collapsable]="true" [_config]="properties"
style="padding-left: 10px; padding-right: 10px; display: block" [style.height.px]="_propertiesClass?.collapsed ? '30' : '90'">
</dashboard-widget-wrapper> </dashboard-widget-wrapper>
<widget-content [widgets]="widgets" [originalConfig]="tab.originalConfig" [context]="tab.context"> <widget-content [widgets]="widgets" [originalConfig]="tab.originalConfig" [context]="tab.context">
</widget-content> </widget-content>
@@ -27,4 +32,30 @@ import { WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
}) })
export class DashboardHomeContainer extends DashboardWidgetContainer { export class DashboardHomeContainer extends DashboardWidgetContainer {
@Input() private properties: WidgetConfig; @Input() private properties: WidgetConfig;
@ViewChild('propertiesClass') private _propertiesClass: DashboardWidgetWrapper;
constructor(
@Inject(forwardRef(() => ChangeDetectorRef)) _cd: ChangeDetectorRef,
@Inject(forwardRef(() => DashboardServiceInterface)) protected dashboardService: DashboardServiceInterface,
) {
super(_cd);
}
ngAfterContentInit() {
let collapsedVal = this.dashboardService.getSettings<string>(`${this.properties.context}.properties`);
if (collapsedVal === 'collapsed') {
this._propertiesClass.collapsed = true;
}
this.dashboardService.angularEventingService.onAngularEvent(this.dashboardService.getUnderlyingUri(), event => {
if (event.event === AngularEventType.COLLAPSE_WIDGET && this._propertiesClass && event.payload === this._propertiesClass.guid) {
this._propertiesClass.collapsed = !this._propertiesClass.collapsed;
this._cd.detectChanges();
this.dashboardService.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, {
key: `dashboard.${this.properties.context}.properties`,
value: this._propertiesClass.collapsed ? 'collapsed' : true
});
}
});
}
} }

View File

@@ -43,7 +43,7 @@ export class DashboardWidgetContainer extends DashboardTab implements OnDestroy,
private _scrollableElement: ScrollableElement; private _scrollableElement: ScrollableElement;
@ViewChild(WidgetContent) private _widgetContent: WidgetContent; @ViewChild(WidgetContent) protected _widgetContent: WidgetContent;
@ViewChild('scrollable', { read: ElementRef }) private _scrollable: ElementRef; @ViewChild('scrollable', { read: ElementRef }) private _scrollable: ElementRef;
@ViewChild('scrollContainer', { read: ElementRef }) private _scrollContainer: ElementRef; @ViewChild('scrollContainer', { read: ElementRef }) private _scrollContainer: ElementRef;

View File

@@ -13,6 +13,8 @@
<span *ngIf="_config.icon" [ngClass]="['icon', _config.icon]" style="display: inline-block; padding: 10px; margin-left: 5px"></span> <span *ngIf="_config.icon" [ngClass]="['icon', _config.icon]" style="display: inline-block; padding: 10px; margin-left: 5px"></span>
</div> </div>
</div> </div>
<ng-template component-host> <ng-template [ngIf]="!collapsed">
<ng-template component-host>
</ng-template>
</ng-template> </ng-template>
</div> </div>

View File

@@ -14,7 +14,7 @@ import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost
import { WidgetConfig, WIDGET_CONFIG, IDashboardWidget } from 'sql/parts/dashboard/common/dashboardWidget'; import { WidgetConfig, WIDGET_CONFIG, IDashboardWidget } from 'sql/parts/dashboard/common/dashboardWidget';
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry'; import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
import { error } from 'sql/base/common/log'; import { error } from 'sql/base/common/log';
import { RefreshWidgetAction, ToggleMoreWidgetAction, DeleteWidgetAction } from 'sql/parts/dashboard/common/actions'; import { RefreshWidgetAction, ToggleMoreWidgetAction, DeleteWidgetAction, CollapseWidgetAction } from 'sql/parts/dashboard/common/actions';
/* Widgets */ /* Widgets */
import { PropertiesWidgetComponent } from 'sql/parts/dashboard/widgets/properties/propertiesWidget.component'; import { PropertiesWidgetComponent } from 'sql/parts/dashboard/widgets/properties/propertiesWidget.component';
@@ -32,6 +32,8 @@ import * as themeColors from 'vs/workbench/common/theme';
import { Action } from 'vs/base/common/actions'; import { Action } from 'vs/base/common/actions';
import { Registry } from 'vs/platform/registry/common/platform'; import { Registry } from 'vs/platform/registry/common/platform';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { memoize } from 'vs/base/common/decorators';
import { generateUuid } from 'vs/base/common/uuid';
const componentMap: { [x: string]: Type<IDashboardWidget> } = { const componentMap: { [x: string]: Type<IDashboardWidget> } = {
'properties-widget': PropertiesWidgetComponent, 'properties-widget': PropertiesWidgetComponent,
@@ -45,8 +47,32 @@ const componentMap: { [x: string]: Type<IDashboardWidget> } = {
selector: 'dashboard-widget-wrapper', selector: 'dashboard-widget-wrapper',
templateUrl: decodeURI(require.toUrl('sql/parts/dashboard/contents/dashboardWidgetWrapper.component.html')) templateUrl: decodeURI(require.toUrl('sql/parts/dashboard/contents/dashboardWidgetWrapper.component.html'))
}) })
export class DashboardWidgetWrapper implements AfterContentInit, OnInit, OnDestroy { export class DashboardWidgetWrapper implements OnInit, OnDestroy {
@Input() private _config: WidgetConfig; @Input() private _config: WidgetConfig;
@Input() private collapsable = false;
private _collapsed = false;
public get collapsed(): boolean {
return this._collapsed;
}
public set collapsed(val: boolean) {
if (val === this._collapsed) {
return;
}
this._collapsed = val;
this._changeref.detectChanges();
if (!val) {
this.loadWidget();
}
}
@memoize
public get guid(): string {
return generateUuid();
}
private _themeDispose: IDisposable; private _themeDispose: IDisposable;
private _actions: Array<Action>; private _actions: Array<Action>;
private _component: IDashboardWidget; private _component: IDashboardWidget;
@@ -71,12 +97,17 @@ export class DashboardWidgetWrapper implements AfterContentInit, OnInit, OnDestr
}); });
} }
ngAfterContentInit() { ngAfterViewInit() {
this.updateTheme(this._bootstrap.themeService.getColorTheme()); this.updateTheme(this._bootstrap.themeService.getColorTheme());
this.loadWidget(); if (this.componentHost) {
this.loadWidget();
}
this._changeref.detectChanges(); this._changeref.detectChanges();
this._actionbar = new ActionBar(this._actionbarRef.nativeElement); this._actionbar = new ActionBar(this._actionbarRef.nativeElement);
if (this._actions) { if (this._actions) {
if (this.collapsable) {
this._actionbar.push(this._bootstrap.instantiationService.createInstance(CollapseWidgetAction, this._bootstrap.getUnderlyingUri(), this.guid, this.collapsed), { icon: true, label: false });
}
this._actionbar.push(this._bootstrap.instantiationService.createInstance(ToggleMoreWidgetAction, this._actions, this._component.actionsContext), { icon: true, label: false }); this._actionbar.push(this._bootstrap.instantiationService.createInstance(ToggleMoreWidgetAction, this._actions, this._component.actionsContext), { icon: true, label: false });
} }
this.layout(); this.layout();

View File

@@ -35,12 +35,11 @@ export class DatabaseDashboardPage extends DashboardPage implements OnInit {
constructor( constructor(
@Inject(forwardRef(() => IBreadcrumbService)) private _breadcrumbService: IBreadcrumbService, @Inject(forwardRef(() => IBreadcrumbService)) private _breadcrumbService: IBreadcrumbService,
@Inject(BOOTSTRAP_SERVICE_ID) bootstrapService: IBootstrapService,
@Inject(forwardRef(() => DashboardServiceInterface)) dashboardService: DashboardServiceInterface, @Inject(forwardRef(() => DashboardServiceInterface)) dashboardService: DashboardServiceInterface,
@Inject(forwardRef(() => ChangeDetectorRef)) _cd: ChangeDetectorRef, @Inject(forwardRef(() => ChangeDetectorRef)) _cd: ChangeDetectorRef,
@Inject(forwardRef(() => ElementRef)) el: ElementRef @Inject(forwardRef(() => ElementRef)) el: ElementRef
) { ) {
super(dashboardService, bootstrapService, el, _cd); super(dashboardService, el, _cd);
this._register(dashboardService.onUpdatePage(() => { this._register(dashboardService.onUpdatePage(() => {
this.refresh(true); this.refresh(true);
this._cd.detectChanges(); this._cd.detectChanges();

View File

@@ -12,6 +12,10 @@ export const databaseDashboardPropertiesSchema: IJSONSchema = {
default: true, default: true,
oneOf: <IJSONSchema[]>[ oneOf: <IJSONSchema[]>[
{ type: 'boolean' }, { type: 'boolean' },
{
type: 'string',
enum: ['collapsed']
},
{ {
type: 'array', type: 'array',
items: { items: {

View File

@@ -36,12 +36,11 @@ export class ServerDashboardPage extends DashboardPage implements OnInit {
constructor( constructor(
@Inject(forwardRef(() => IBreadcrumbService)) private breadcrumbService: IBreadcrumbService, @Inject(forwardRef(() => IBreadcrumbService)) private breadcrumbService: IBreadcrumbService,
@Inject(BOOTSTRAP_SERVICE_ID) bootstrapService: IBootstrapService,
@Inject(forwardRef(() => DashboardServiceInterface)) dashboardService: DashboardServiceInterface, @Inject(forwardRef(() => DashboardServiceInterface)) dashboardService: DashboardServiceInterface,
@Inject(forwardRef(() => ChangeDetectorRef)) _cd: ChangeDetectorRef, @Inject(forwardRef(() => ChangeDetectorRef)) _cd: ChangeDetectorRef,
@Inject(forwardRef(() => ElementRef)) el: ElementRef @Inject(forwardRef(() => ElementRef)) el: ElementRef
) { ) {
super(dashboardService, bootstrapService, el, _cd); super(dashboardService, el, _cd);
// revert back to default database // revert back to default database
this._letDashboardPromise = this.dashboardService.connectionManagementService.changeDatabase('master'); this._letDashboardPromise = this.dashboardService.connectionManagementService.changeDatabase('master');
} }

View File

@@ -20,6 +20,10 @@ export const serverDashboardPropertiesSchema: IJSONSchema = {
default: true, default: true,
oneOf: [ oneOf: [
{ type: 'boolean' }, { type: 'boolean' },
{
type: 'string',
enum: ['collapsed']
},
{ {
type: 'object', type: 'object',
properties: { properties: {

View File

@@ -20,7 +20,7 @@ import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils';
import { IInsightsDialogService } from 'sql/parts/insights/common/interfaces'; import { IInsightsDialogService } from 'sql/parts/insights/common/interfaces';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { AngularEventType, IAngularEvent } from 'sql/services/angularEventing/angularEventingService'; import { AngularEventType, IAngularEvent, IAngularEventingService } from 'sql/services/angularEventing/angularEventingService';
import { IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry'; import { IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
import { PinConfig } from 'sql/parts/dashboard/common/dashboardWidget'; import { PinConfig } from 'sql/parts/dashboard/common/dashboardWidget';
@@ -137,6 +137,7 @@ export class DashboardServiceInterface implements OnDestroy {
private _commandService: ICommandService; private _commandService: ICommandService;
private _dashboardWebviewService: IDashboardWebviewService; private _dashboardWebviewService: IDashboardWebviewService;
private _partService: IPartService; private _partService: IPartService;
private _angularEventingService: IAngularEventingService;
private _updatePage = new Emitter<void>(); private _updatePage = new Emitter<void>();
public readonly onUpdatePage: Event<void> = this._updatePage.event; public readonly onUpdatePage: Event<void> = this._updatePage.event;
@@ -171,6 +172,7 @@ export class DashboardServiceInterface implements OnDestroy {
this._commandService = this._bootstrapService.commandService; this._commandService = this._bootstrapService.commandService;
this._dashboardWebviewService = this._bootstrapService.dashboardWebviewService; this._dashboardWebviewService = this._bootstrapService.dashboardWebviewService;
this._partService = this._bootstrapService.partService; this._partService = this._bootstrapService.partService;
this._angularEventingService = this._bootstrapService.angularEventingService;
} }
ngOnDestroy() { ngOnDestroy() {
@@ -241,6 +243,10 @@ export class DashboardServiceInterface implements OnDestroy {
return this._capabilitiesService; return this._capabilitiesService;
} }
public get angularEventingService(): IAngularEventingService {
return this._angularEventingService;
}
/** /**
* Set the selector for this dashboard instance, should only be set once * Set the selector for this dashboard instance, should only be set once
*/ */

View File

@@ -43,6 +43,8 @@ export class ExplorerWidget extends DashboardWidget implements IDashboardWidget,
private _treeDataSource = new ExplorerDataSource(); private _treeDataSource = new ExplorerDataSource();
private _treeFilter = new ExplorerFilter(); private _treeFilter = new ExplorerFilter();
private _inited = false;
@ViewChild('input') private _inputContainer: ElementRef; @ViewChild('input') private _inputContainer: ElementRef;
@ViewChild('table') private _tableContainer: ElementRef; @ViewChild('table') private _tableContainer: ElementRef;
@@ -58,6 +60,8 @@ export class ExplorerWidget extends DashboardWidget implements IDashboardWidget,
} }
ngOnInit() { ngOnInit() {
this._inited = true;
let inputOptions: IInputOptions = { let inputOptions: IInputOptions = {
placeholder: this._config.context === 'database' ? nls.localize('seachObjects', 'Search by name of type (a:, t:, v:, f:, or sp:)') : nls.localize('searchDatabases', 'Search databases') placeholder: this._config.context === 'database' ? nls.localize('seachObjects', 'Search by name of type (a:, t:, v:, f:, or sp:)') : nls.localize('searchDatabases', 'Search databases')
}; };
@@ -120,6 +124,8 @@ export class ExplorerWidget extends DashboardWidget implements IDashboardWidget,
} }
public layout(): void { public layout(): void {
this._tree.layout(getContentHeight(this._tableContainer.nativeElement)); if (this._inited) {
this._tree.layout(getContentHeight(this._tableContainer.nativeElement));
}
} }
} }

View File

@@ -46,6 +46,8 @@ export class TasksWidget extends DashboardWidget implements IDashboardWidget, On
private _scrollableElement: ScrollableElement; private _scrollableElement: ScrollableElement;
private $container: Builder; private $container: Builder;
private _inited = false;
@ViewChild('container', { read: ElementRef }) private _container: ElementRef; @ViewChild('container', { read: ElementRef }) private _container: ElementRef;
constructor( constructor(
@@ -67,6 +69,7 @@ export class TasksWidget extends DashboardWidget implements IDashboardWidget, On
} }
ngOnInit() { ngOnInit() {
this._inited = true;
this._register(registerThemingParticipant(this.registerThemeing)); this._register(registerThemingParticipant(this.registerThemeing));
this._computeContainer(); this._computeContainer();
@@ -137,11 +140,13 @@ export class TasksWidget extends DashboardWidget implements IDashboardWidget, On
} }
public layout(): void { public layout(): void {
this._computeContainer(); if (this._inited) {
// Update scrollbar this._computeContainer();
this._scrollableElement.setScrollDimensions({ // Update scrollbar
width: DOM.getContentWidth(this._container.nativeElement), this._scrollableElement.setScrollDimensions({
scrollWidth: DOM.getContentWidth(this.$container.getHTMLElement()) + 18 // right padding width: DOM.getContentWidth(this._container.nativeElement),
}); scrollWidth: DOM.getContentWidth(this.$container.getHTMLElement()) + 18 // right padding
});
}
} }
} }

View File

@@ -17,7 +17,8 @@ export enum AngularEventType {
DELETE_WIDGET, DELETE_WIDGET,
PINUNPIN_TAB, PINUNPIN_TAB,
NEW_TABS, NEW_TABS,
CLOSE_TAB CLOSE_TAB,
COLLAPSE_WIDGET
} }
export interface IDeleteWidgetPayload { export interface IDeleteWidgetPayload {