mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-27 01:25:36 -05:00
dashboard improvement (#9730)
* dashboard improvement - WIP (#8836) * wip * wip * tabgroup * tab group * agent views * clean up * formats * feedback * fix error * contribute top level server/db dashboard tab (#8868) * tabbedPanel component (#8861) * tabbed panel * tabbed panel * fix errors * revert main.ts changes * use margin * address comments * remove orientation property * content tab group (#8878) * add databases tab * use more extensible approach * remove unnecessary code * add when expression * objects tab for database dashboard (#8892) * fix build errors * fix build error * Dashboard toolbar (#9118) * remove old toolbar with only edit and refresh * remove tasks widgets from server and databases dashboards * adding toolbar to dashboardpage and clicking new query works * restore and new notebook now do something * add backup to toolbar for database dashboards * new notebook connects to db * only show backup and restore for non-azure * new backup and restore svgs * clean up * got toolbar actions to show up from contribution * some cleanup and add database dashboard toolbar contributions * don't show all tasks when there should be no tasks * fix toolbar showing multiple times when switching opening another dashboard from OE * only show toolbar for home page * update to new icons - same icons for light and dark theme * don't show separator if there aren't any actions * read toolbar actions from tasks-widget * remove tasks widget from home dashboard page * show extension's actions in toolbar * clean up * more cleaning up * fix extension actions not always loading the first time * add configure dashboard * remove old edit icon css * change tasks back to original order * make sure tasks widget is the one being removed * collapsible tab panel (#9221) * collapsible vertical tab panel * fix lint error * comments batch 1 * pr comments * update new query icon (#9351) * Update toolbar actions (#9313) * remove edit and configure dashboard and add refresh to toolbar for other dashboard pages too * Add refresh for tabs that have container type with refresh implemented * change refresh to only refresh the current tab * remove map for tab to actions * add back configure dashboard to home toolbar * check if index is -1 before trying to remove tasks widget from widgets * Move objects widget back to database home tab (#9432) * move objects widget back to database home tab and reorder toolbar * change order of actions back to previous order * Allow extensions to add actions to home toolbar (#9269) * add support for extensions to add actions to home toolbar * fix spacing * use menu contribution point * undo previous changes that added dashboardToolbarHomeAction contribution * remove home from name * add context key for tab name * allow actions to also be added to the toolbar of other tabs * add extension contributed actions even if no tasks-widget * fix refresh being added twice after merging * hide the tab list when collapsed (#9529) * update the order of css selectors (#9606) * Update dashboard style to be closer to mockups (#9570) * update style to be closer to mockups * tab panel styling * change back tab styling for tabs in a tab contributed by an extension * change color of borders when theme changes * set dark theme active tab background to same as OE for now * update border colors * move colors to theme file * fix a few issues (#9690) * couple fixes * comments * small dashboard toolbar fixes (#9695) * fix backup icon in toolbar * fix database page toolbar border color * add back center center in common-icons.css (#9703) * change padding so bottom border shows again (#9710) * tab panel fixes (#9724) * tab panel fixes * fix package.nls.json * feedbacks (#9761) * feedbacks * remove comments Co-authored-by: Kim Santiago <31145923+kisantia@users.noreply.github.com>
This commit is contained in:
@@ -10,8 +10,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
|
||||
import { IModelViewService } from 'sql/platform/modelComponents/browser/modelViewService';
|
||||
import { IItemConfig, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { IModelView } from 'sql/platform/model/browser/modelViewService';
|
||||
import { IItemConfig, IComponentShape, IModelView } from 'sql/platform/model/browser/modelViewService';
|
||||
import { find } from 'vs/base/common/arrays';
|
||||
|
||||
|
||||
|
||||
@@ -249,6 +249,13 @@ class ModelBuilderImpl implements azdata.ModelBuilder {
|
||||
return builder;
|
||||
}
|
||||
|
||||
tabbedPanel(): azdata.TabbedPanelComponentBuilder {
|
||||
let id = this.getNextComponentId();
|
||||
let builder = new TabbedPanelComponentBuilder(new TabbedPanelComponentWrapper(this._proxy, this._handle, id));
|
||||
this._componentBuilders.set(id, builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
getComponentBuilder<T extends azdata.Component>(component: ComponentWrapper, id: string): ComponentBuilderImpl<T> {
|
||||
let componentBuilder: ComponentBuilderImpl<T> = new ComponentBuilderImpl<T>(component);
|
||||
this._componentBuilders.set(id, componentBuilder);
|
||||
@@ -486,6 +493,32 @@ class ToolbarContainerBuilder extends GenericContainerBuilder<azdata.ToolbarCont
|
||||
}
|
||||
}
|
||||
|
||||
class TabbedPanelComponentBuilder extends ContainerBuilderImpl<azdata.TabbedPanelComponent, azdata.TabbedPanelLayout, any> implements azdata.TabbedPanelComponentBuilder {
|
||||
withTabs(items: (azdata.Tab | azdata.TabGroup)[]): azdata.ContainerBuilder<azdata.TabbedPanelComponent, azdata.TabbedPanelLayout, any> {
|
||||
const itemConfigs = [];
|
||||
items.forEach(item => {
|
||||
if (item && 'tabs' in item) {
|
||||
item.tabs.forEach(tab => {
|
||||
itemConfigs.push(this.toItemConfig(tab.content, tab.title, tab.id, item.title));
|
||||
});
|
||||
} else {
|
||||
const tab = <azdata.Tab>item;
|
||||
itemConfigs.push(this.toItemConfig(tab.content, tab.title, tab.id));
|
||||
}
|
||||
});
|
||||
this._component.itemConfigs = itemConfigs;
|
||||
return this;
|
||||
}
|
||||
|
||||
toItemConfig(content: azdata.Component, title: string, id?: string, group?: string): InternalItemConfig {
|
||||
return new InternalItemConfig(content as ComponentWrapper, {
|
||||
title: title,
|
||||
group: group,
|
||||
id: id
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class LoadingComponentBuilder extends ComponentBuilderImpl<azdata.LoadingComponent> implements azdata.LoadingComponentBuilder {
|
||||
withItem(component: azdata.Component) {
|
||||
this.component().component = component;
|
||||
@@ -1688,6 +1721,19 @@ class RadioCardGroupComponentWrapper extends ComponentWrapper implements azdata.
|
||||
}
|
||||
}
|
||||
|
||||
class TabbedPanelComponentWrapper extends ComponentWrapper implements azdata.TabbedPanelComponent {
|
||||
constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {
|
||||
super(proxy, handle, ModelComponentTypes.TabbedPanel, id);
|
||||
this.properties = {};
|
||||
this._emitterMap.set(ComponentEventType.onDidChange, new Emitter<string>());
|
||||
}
|
||||
|
||||
public get onTabChanged(): vscode.Event<string> {
|
||||
let emitter = this._emitterMap.get(ComponentEventType.onDidChange);
|
||||
return emitter && emitter.event;
|
||||
}
|
||||
}
|
||||
|
||||
class GroupContainerComponentWrapper extends ComponentWrapper implements azdata.GroupContainer {
|
||||
constructor(proxy: MainThreadModelViewShape, handle: number, type: ModelComponentTypes, id: string) {
|
||||
super(proxy, handle, type, id);
|
||||
|
||||
@@ -552,7 +552,8 @@ export function createAdsApiFactory(accessor: ServicesAccessor): IAdsExtensionAp
|
||||
AgentSubSystem: sqlExtHostTypes.AgentSubSystem,
|
||||
ExtensionNodeType: sqlExtHostTypes.ExtensionNodeType,
|
||||
ColumnSizingMode: sqlExtHostTypes.ColumnSizingMode,
|
||||
DatabaseEngineEdition: sqlExtHostTypes.DatabaseEngineEdition
|
||||
DatabaseEngineEdition: sqlExtHostTypes.DatabaseEngineEdition,
|
||||
TabOrientation: sqlExtHostTypes.TabOrientation
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -174,6 +174,7 @@ export enum ModelComponentTypes {
|
||||
Hyperlink,
|
||||
Image,
|
||||
RadioCardGroup,
|
||||
TabbedPanel,
|
||||
Separator
|
||||
}
|
||||
|
||||
@@ -828,3 +829,13 @@ export type QueryEventType =
|
||||
| 'queryStop'
|
||||
| 'executionPlan'
|
||||
| 'visualize';
|
||||
|
||||
export enum TabOrientation {
|
||||
Vertical = 'vertical',
|
||||
Horizontal = 'horizontal'
|
||||
}
|
||||
|
||||
|
||||
export interface TabbedPanelLayout {
|
||||
orientation: TabOrientation;
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ export class InsightAction extends Action {
|
||||
|
||||
export class ConfigureDashboardAction extends Task {
|
||||
public static readonly ID = 'configureDashboard';
|
||||
public static readonly LABEL = nls.localize('configureDashboard', "Learn How To Configure The Dashboard");
|
||||
public static readonly LABEL = nls.localize('configureDashboardLearnMore', "Learn More");
|
||||
public static readonly ICON = 'configure-dashboard';
|
||||
private static readonly configHelpUri = 'https://aka.ms/sqldashboardconfig';
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.tabbedpanel-component {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tabbedpanel-component .tabbedPanel .tabContainer {
|
||||
border-style: solid;
|
||||
border-color: rgb(237, 235, 233);
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
.vs-dark .tabbedpanel-component .tabbedPanel .tabContainer, .hc-black .tabbedpanel-component .tabbedPanel .tabContainer {
|
||||
border-color: rgba(128, 128, 128, 0.5);;
|
||||
}
|
||||
|
||||
.tabbedpanel-component .tabbedPanel.vertical .tabContainer {
|
||||
border-right-width: 1px;
|
||||
}
|
||||
|
||||
.tabbedpanel-component .tabbedPanel.horizontal .tabContainer {
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
.tabbedpanel-component .tabbedPanel .tab>.tabLabel.active {
|
||||
border-bottom: 0px solid;
|
||||
}
|
||||
|
||||
.tabbedpanel-component .tabbedPanel.vertical .tabList .tab-header {
|
||||
line-height: 35px;
|
||||
}
|
||||
|
||||
.tabbedpanel-component .tabbedPanel.horizontal .tabList .tab-header {
|
||||
border-color: rgb(214, 214, 214);
|
||||
border-width: 0 1px 0 0;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
.tabbedpanel-component .tabbedPanel .tabList .tab .tabLabel {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.tabbedpanel-component .tabList .tab-header.active {
|
||||
background-color: rgb(237, 235, 233);
|
||||
}
|
||||
|
||||
.vs-dark .tabbedpanel-component .tabList .tab-header.active, .hc-black .tabbedpanel-component .tabList .tab-header.active {
|
||||
background-color: rgba(128, 128, 128, 0.5);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<!--
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
<div class="tabbedpanel-component">
|
||||
<panel (onTabChange)="handleTabChange($event)">
|
||||
<tab [visibilityType]="'visibility'" *ngFor="let tab of tabs" [title]="tab.title" class="fullsize"
|
||||
[identifier]="tab.id" [type]="tab.type">
|
||||
<ng-template>
|
||||
<ng-container *ngIf="tab.type === 'tab'">
|
||||
{{tab.title}}
|
||||
<model-component-wrapper [descriptor]="tab.content" [modelStore]="modelStore">
|
||||
</model-component-wrapper>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
</tab>
|
||||
</panel>
|
||||
</div>
|
||||
@@ -0,0 +1,96 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, forwardRef, Inject, Input, OnDestroy, ViewChild } from '@angular/core';
|
||||
import { NavigationBarLayout, PanelComponent } from 'sql/base/browser/ui/panel/panel.component';
|
||||
import { TabType } from 'sql/base/browser/ui/panel/tab.component';
|
||||
// eslint-disable-next-line code-import-patterns
|
||||
import { TabOrientation, TabbedPanelLayout } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { ContainerBase } from 'sql/workbench/browser/modelComponents/componentBase';
|
||||
import { ComponentEventType, IComponent, IComponentDescriptor, IModelStore } from 'sql/platform/dashboard/browser/interfaces';
|
||||
import 'vs/css!./media/tabbedPanel';
|
||||
|
||||
export interface TabConfig {
|
||||
title: string;
|
||||
id?: string;
|
||||
group: string;
|
||||
}
|
||||
|
||||
interface Tab {
|
||||
title: string;
|
||||
content?: IComponentDescriptor;
|
||||
id?: string;
|
||||
type: TabType;
|
||||
}
|
||||
|
||||
@Component({
|
||||
templateUrl: decodeURI(require.toUrl('./tabbedPanel.component.html'))
|
||||
})
|
||||
export default class TabbedPanelComponent extends ContainerBase<TabConfig> implements IComponent, OnDestroy, AfterViewInit {
|
||||
@Input() descriptor: IComponentDescriptor;
|
||||
@Input() modelStore: IModelStore;
|
||||
@ViewChild(PanelComponent) private _panel: PanelComponent;
|
||||
|
||||
private _tabs: Tab[] = [];
|
||||
private _itemIndexToProcess: number = 0;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
|
||||
@Inject(forwardRef(() => ElementRef)) el: ElementRef) {
|
||||
super(changeRef, el);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.baseInit();
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.baseDestroy();
|
||||
}
|
||||
|
||||
setLayout(layout: TabbedPanelLayout): void {
|
||||
this._panel.options = {
|
||||
showTabsWhenOne: true,
|
||||
layout: layout.orientation === TabOrientation.Horizontal ? NavigationBarLayout.horizontal : NavigationBarLayout.vertical,
|
||||
showIcon: false
|
||||
};
|
||||
}
|
||||
|
||||
handleTabChange(event: any): void {
|
||||
this.fireEvent({
|
||||
eventType: ComponentEventType.onDidChange,
|
||||
args: event.identifier
|
||||
});
|
||||
}
|
||||
|
||||
get tabs(): Tab[] {
|
||||
if (this.items.length > this._itemIndexToProcess) {
|
||||
let currentGroup: string | undefined = this.items.length === 1 ? undefined : this.items[this._itemIndexToProcess - 1].config.group;
|
||||
for (let i = this._itemIndexToProcess; i < this.items.length; i++) {
|
||||
const item = this.items[i];
|
||||
if (item.config.group !== currentGroup) {
|
||||
currentGroup = item.config.group;
|
||||
if (currentGroup) {
|
||||
this._tabs.push({
|
||||
title: currentGroup,
|
||||
type: 'group-header'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this._tabs.push({
|
||||
title: item.config.title,
|
||||
id: item.config.id,
|
||||
content: item.descriptor,
|
||||
type: 'tab'
|
||||
});
|
||||
}
|
||||
this._itemIndexToProcess = this.items.length;
|
||||
}
|
||||
return this._tabs;
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ export class DashboardNavSection extends DashboardTab implements OnDestroy, OnCh
|
||||
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
private readonly panelOpt: IPanelOptions = {
|
||||
layout: NavigationBarLayout.vertical
|
||||
layout: NavigationBarLayout.horizontal
|
||||
};
|
||||
|
||||
// a set of config modifiers
|
||||
|
||||
@@ -6,15 +6,12 @@
|
||||
import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import * as nls from 'vs/nls';
|
||||
import { createCSSRule, asCSSUrl } from 'vs/base/browser/dom';
|
||||
import { IdGenerator } from 'vs/base/common/idGenerator';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
|
||||
import { NavSectionConfig, IUserFriendlyIcon } from 'sql/workbench/contrib/dashboard/browser/core/dashboardWidget';
|
||||
import { NavSectionConfig } from 'sql/workbench/contrib/dashboard/browser/core/dashboardWidget';
|
||||
import { registerContainerType, generateNavSectionContainerTypeSchemaProperties } from 'sql/platform/dashboard/common/dashboardContainerRegistry';
|
||||
import { WIDGETS_CONTAINER, validateWidgetContainerContribution } from 'sql/workbench/contrib/dashboard/browser/containers/dashboardWidgetContainer.contribution';
|
||||
import { GRID_CONTAINER, validateGridContainerContribution } from 'sql/workbench/contrib/dashboard/browser/containers/dashboardGridContainer.contribution';
|
||||
import { values } from 'vs/base/common/collections';
|
||||
import { createCSSRuleForIcon, isValidIcon } from 'sql/workbench/contrib/dashboard/browser/dashboardIconUtil';
|
||||
|
||||
export const NAV_SECTION = 'nav-section';
|
||||
|
||||
@@ -64,38 +61,6 @@ const NavSectionSchema: IJSONSchema = {
|
||||
|
||||
registerContainerType(NAV_SECTION, NavSectionSchema);
|
||||
|
||||
function isValidIcon(icon: IUserFriendlyIcon, extension: IExtensionPointUser<any>): boolean {
|
||||
if (typeof icon === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
if (typeof icon === 'string') {
|
||||
return true;
|
||||
} else if (typeof icon.dark === 'string' && typeof icon.light === 'string') {
|
||||
return true;
|
||||
}
|
||||
extension.collector.error(nls.localize('opticon', "property `icon` can be omitted or must be either a string or a literal like `{dark, light}`"));
|
||||
return false;
|
||||
}
|
||||
|
||||
const ids = new IdGenerator('contrib-dashboardNavSection-icon-');
|
||||
|
||||
function createCSSRuleForIcon(icon: IUserFriendlyIcon, extension: IExtensionPointUser<any>): string {
|
||||
let iconClass: string;
|
||||
if (icon) {
|
||||
iconClass = ids.nextId();
|
||||
if (typeof icon === 'string') {
|
||||
const path = resources.joinPath(extension.description.extensionLocation, icon);
|
||||
createCSSRule(`.codicon.${iconClass}`, `background-image: ${asCSSUrl(path)}`);
|
||||
} else {
|
||||
const light = resources.joinPath(extension.description.extensionLocation, icon.light);
|
||||
const dark = resources.joinPath(extension.description.extensionLocation, icon.dark);
|
||||
createCSSRule(`.codicon.${iconClass}`, `background-image: ${asCSSUrl(light)}`);
|
||||
createCSSRule(`.vs-dark .codicon.${iconClass}, .hc-black .codicon.${iconClass}`, `background-image: ${asCSSUrl(dark)}`);
|
||||
}
|
||||
}
|
||||
return iconClass;
|
||||
}
|
||||
|
||||
export function validateNavSectionContributionAndRegisterIcon(extension: IExtensionPointUser<any>, navSectionConfigs: NavSectionConfig[]): boolean {
|
||||
let result = true;
|
||||
navSectionConfigs.forEach(section => {
|
||||
|
||||
@@ -7,9 +7,10 @@
|
||||
<div style="display: flex; flex-flow: column; overflow: hidden; height: 100%; width: 100%">
|
||||
|
||||
<div #header>
|
||||
<div style="display: flex; flex: 0 0; padding: 3px 0 3px 0;flex: 0 0;">
|
||||
<div 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>
|
||||
<span *ngIf="_config.name" style="margin-left: 5px;flex:1 1;">{{_config.name}}</span>
|
||||
<span *ngIf="!_config.name" style="flex:1 1"></span>
|
||||
<span #actionbar style="flex: 0 0 auto; align-self: end"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -162,7 +162,7 @@ export class DashboardWidgetWrapper extends AngularDisposable implements OnInit
|
||||
// If _config.name is not set, set it to _config.widget.name
|
||||
if (!this._config.name) {
|
||||
const widget = values(this._config.widget)[0];
|
||||
if (widget.name) {
|
||||
if (widget && widget.name) {
|
||||
this._config.name = widget.name;
|
||||
}
|
||||
}
|
||||
@@ -221,7 +221,7 @@ export class DashboardWidgetWrapper extends AngularDisposable implements OnInit
|
||||
private updateTheme(theme: IColorTheme): void {
|
||||
const el = <HTMLElement>this._ref.nativeElement;
|
||||
const headerEl: HTMLElement = this.header.nativeElement;
|
||||
let borderColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true);
|
||||
let borderColor = theme.getColor(themeColors.DASHBOARD_BORDER);
|
||||
let backgroundColor = theme.getColor(colors.editorBackground, true);
|
||||
const foregroundColor = theme.getColor(themeColors.SIDE_BAR_FOREGROUND, true);
|
||||
const border = theme.getColor(colors.contrastBorder, true);
|
||||
@@ -249,18 +249,12 @@ export class DashboardWidgetWrapper extends AngularDisposable implements OnInit
|
||||
el.style.borderWidth = '1px';
|
||||
el.style.borderStyle = 'solid';
|
||||
} else if (borderColor) {
|
||||
borderString = borderColor.toString();
|
||||
el.style.border = '3px solid ' + borderColor.toString();
|
||||
borderString = borderColor;
|
||||
el.style.border = '1px solid ' + borderColor;
|
||||
} else {
|
||||
el.style.border = 'none';
|
||||
}
|
||||
|
||||
if (borderString) {
|
||||
headerEl.style.backgroundColor = borderString;
|
||||
} else {
|
||||
headerEl.style.backgroundColor = '';
|
||||
}
|
||||
|
||||
if (this._config.fontSize) {
|
||||
headerEl.style.fontSize = this._config.fontSize;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { INewDashboardTabDialogService } from 'sql/workbench/services/dashboard/
|
||||
import { IDashboardTab } from 'sql/workbench/services/dashboard/browser/common/interfaces';
|
||||
import { find, firstIndex } from 'vs/base/common/arrays';
|
||||
import { CellContext } from 'sql/workbench/contrib/notebook/browser/cellViews/codeActions';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
export class EditDashboardAction extends Action {
|
||||
|
||||
@@ -25,7 +26,7 @@ export class EditDashboardAction extends Action {
|
||||
|
||||
constructor(
|
||||
private editFn: () => void,
|
||||
private context: any //this
|
||||
private context: any
|
||||
) {
|
||||
super(EditDashboardAction.ID, EditDashboardAction.EDITLABEL, EditDashboardAction.ICON);
|
||||
}
|
||||
@@ -59,7 +60,7 @@ export class RefreshWidgetAction extends Action {
|
||||
|
||||
constructor(
|
||||
private refreshFn: () => void,
|
||||
private context: any // this
|
||||
private context: any
|
||||
) {
|
||||
super(RefreshWidgetAction.ID, RefreshWidgetAction.LABEL, RefreshWidgetAction.ICON);
|
||||
}
|
||||
@@ -74,6 +75,29 @@ export class RefreshWidgetAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
export class ToolbarAction extends Action {
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
cssClass: string,
|
||||
private runFn: (id: string) => void,
|
||||
private context: any, // this
|
||||
private logService: ILogService
|
||||
) {
|
||||
super(id, label, cssClass);
|
||||
}
|
||||
|
||||
run(): Promise<boolean> {
|
||||
try {
|
||||
this.runFn.apply(this.context, [this.id]);
|
||||
return Promise.resolve(true);
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ToggleMoreWidgetAction extends Action {
|
||||
|
||||
private static readonly ID = 'toggleMore';
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
-->
|
||||
<panel class="dashboard-panel" (onTabChange)="handleTabChange($event)" (onTabClose)="handleTabClose($event)"
|
||||
[actions]="panelActions">
|
||||
<div #toolbar [style.display]="showToolbar ? 'block': 'none'" class="editor-toolbar">
|
||||
</div>
|
||||
<tab [visibilityType]="'visibility'" *ngFor="let tab of tabs" [title]="tab.title" class="fullsize"
|
||||
[identifier]="tab.id" [canClose]="tab.canClose" [actions]="tab.actions">
|
||||
[identifier]="tab.id" [canClose]="tab.canClose" [actions]="tab.actions" [type]="tab.type" [iconClass]="tab.iconClass">
|
||||
<ng-template>
|
||||
<dashboard-home-container *ngIf="tab.id === 'homeTab'; else not_home" [properties]="propertiesWidget"
|
||||
[tab]="tab">
|
||||
@@ -30,4 +32,4 @@
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</tab>
|
||||
</panel>
|
||||
</panel>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./dashboardPage';
|
||||
import 'vs/css!sql/media/icons/common-icons';
|
||||
import 'sql/workbench/contrib/dashboard/browser/core/dashboardPanelStyles';
|
||||
|
||||
import { Component, Inject, forwardRef, ViewChild, ElementRef, ViewChildren, QueryList, ChangeDetectorRef } from '@angular/core';
|
||||
@@ -12,10 +13,9 @@ import { DashboardServiceInterface } from 'sql/workbench/contrib/dashboard/brows
|
||||
import { CommonServiceInterface, SingleConnectionManagementService } from 'sql/workbench/services/bootstrap/browser/commonServiceInterface.service';
|
||||
import { WidgetConfig, TabConfig, TabSettingConfig } from 'sql/workbench/contrib/dashboard/browser/core/dashboardWidget';
|
||||
import { IPropertiesConfig } from 'sql/workbench/contrib/dashboard/browser/pages/serverDashboardPage.contribution';
|
||||
import { PanelComponent } from 'sql/base/browser/ui/panel/panel.component';
|
||||
import { PanelComponent, NavigationBarLayout } from 'sql/base/browser/ui/panel/panel.component';
|
||||
import { IDashboardRegistry, Extensions as DashboardExtensions } from 'sql/workbench/contrib/dashboard/browser/dashboardRegistry';
|
||||
import { IDashboardTab } from 'sql/workbench/services/dashboard/browser/common/interfaces';
|
||||
import { PinUnpinTabAction, AddFeatureTabAction } from './actions';
|
||||
import { TabComponent, TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||
import { AngularEventType, IAngularEventingService } from 'sql/platform/angularEventing/browser/angularEventingService';
|
||||
import { DashboardTab, IConfigModifierCollection } from 'sql/workbench/contrib/dashboard/browser/core/interfaces';
|
||||
@@ -31,17 +31,31 @@ import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { Action, IAction, IActionViewItem } from 'vs/base/common/actions';
|
||||
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextKeyService, ContextKeyExpr, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { firstIndex, find } from 'vs/base/common/arrays';
|
||||
import { values } from 'vs/base/common/collections';
|
||||
import { RefreshWidgetAction, ToolbarAction } from 'sql/workbench/contrib/dashboard/browser/core/actions';
|
||||
import { Taskbar, ITaskbarContent } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { TaskRegistry } from 'sql/workbench/services/tasks/browser/tasksRegistry';
|
||||
import { MenuRegistry, IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
|
||||
import { fillInActions, LabeledMenuItemActionItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { NAV_SECTION } from 'sql/workbench/contrib/dashboard/browser/containers/dashboardNavSection.contribution';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { DASHBOARD_BORDER } from 'vs/workbench/common/theme';
|
||||
import { IColorTheme } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
|
||||
const dashboardRegistry = Registry.as<IDashboardRegistry>(DashboardExtensions.DashboardContributions);
|
||||
const homeTabGroupId = 'home';
|
||||
|
||||
@Component({
|
||||
selector: 'dashboard-page',
|
||||
@@ -59,12 +73,20 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig
|
||||
|
||||
@ViewChildren(TabChild) private _tabs: QueryList<DashboardTab>;
|
||||
@ViewChild(PanelComponent) private _panel: PanelComponent;
|
||||
@ViewChild('toolbar', { read: ElementRef }) private toolbarContainer: ElementRef;
|
||||
protected toolbar: Taskbar;
|
||||
public showToolbar: boolean;
|
||||
|
||||
private _editEnabled = new Emitter<boolean>();
|
||||
public readonly editEnabled: Event<boolean> = this._editEnabled.event;
|
||||
|
||||
// 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 tabContents = new Map<string, string>();
|
||||
|
||||
static tabName = new RawContextKey<string>('tabName', undefined);
|
||||
private _tabName: IContextKey<string>;
|
||||
|
||||
// a set of config modifiers
|
||||
private readonly _configModifiers: Array<(item: Array<WidgetConfig>, collection: IConfigModifierCollection, context: string) => Array<WidgetConfig>> = [
|
||||
@@ -95,13 +117,23 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) protected dashboardService: DashboardServiceInterface,
|
||||
@Inject(forwardRef(() => ElementRef)) protected _el: ElementRef,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) protected _cd: ChangeDetectorRef,
|
||||
@Inject(IInstantiationService) private instantiationService: IInstantiationService,
|
||||
@Inject(INotificationService) private notificationService: INotificationService,
|
||||
@Inject(IAngularEventingService) private angularEventingService: IAngularEventingService,
|
||||
@Inject(IConfigurationService) private configurationService: IConfigurationService,
|
||||
@Inject(ILogService) private logService: ILogService
|
||||
@Inject(ILogService) private logService: ILogService,
|
||||
@Inject(ICommandService) private commandService: ICommandService,
|
||||
@Inject(IContextKeyService) contextKeyService: IContextKeyService,
|
||||
@Inject(IMenuService) private menuService: IMenuService,
|
||||
@Inject(IKeybindingService) private keybindingService: IKeybindingService,
|
||||
@Inject(IContextMenuService) private contextMenuService: IContextMenuService,
|
||||
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService
|
||||
) {
|
||||
super();
|
||||
this._tabName = DashboardPage.tabName.bindTo(contextKeyService);
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.updateTheme(this.themeService.getColorTheme());
|
||||
}
|
||||
|
||||
protected init() {
|
||||
@@ -113,6 +145,12 @@ 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._originalConfig = objects.deepClone(tempWidgets);
|
||||
let properties = this.getProperties();
|
||||
this._configModifiers.forEach((cb) => {
|
||||
@@ -123,9 +161,134 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig
|
||||
tempWidgets = cb.apply(this, [tempWidgets, this._originalConfig]);
|
||||
});
|
||||
this.propertiesWidget = properties ? properties[0] : undefined;
|
||||
|
||||
this._panel.options = {
|
||||
showTabsWhenOne: true,
|
||||
layout: NavigationBarLayout.vertical,
|
||||
showIcon: true
|
||||
};
|
||||
this.createTabs(tempWidgets);
|
||||
}
|
||||
|
||||
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) => {
|
||||
this.updateTheme(event);
|
||||
}));
|
||||
}
|
||||
|
||||
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 === '');
|
||||
|
||||
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 });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (primary.length > 0) {
|
||||
let separator: HTMLElement = Taskbar.createTaskbarSeparator();
|
||||
content.push({ element: separator });
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
// 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) {
|
||||
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[] {
|
||||
let tasks = TaskRegistry.getTasks();
|
||||
let content;
|
||||
if (types.isArray(toolbarTasks) && toolbarTasks.length > 0) {
|
||||
tasks = toolbarTasks.map(i => {
|
||||
if (types.isString(i)) {
|
||||
if (tasks.some(x => x === i)) {
|
||||
return i;
|
||||
}
|
||||
} else {
|
||||
if (tasks.some(x => x === i.name) && this.contextKeyService.contextMatchesRules(ContextKeyExpr.deserialize(i.when))) {
|
||||
return i.name;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}).filter(i => !!i);
|
||||
content = this.convertTasksToToolbarContent(tasks);
|
||||
} else {
|
||||
content = [];
|
||||
}
|
||||
|
||||
// get extension actions contributed to the page's toolbar
|
||||
this.getExtensionContributedHomeToolbarContent(content);
|
||||
|
||||
const refreshAction = new RefreshWidgetAction(this.refresh, this);
|
||||
content.push({ action: refreshAction });
|
||||
return content;
|
||||
}
|
||||
|
||||
private convertTasksToToolbarContent(tasks: string[]): ITaskbarContent[] {
|
||||
let _tasks = tasks.map(i => MenuRegistry.getCommand(i)).filter(v => !!v);
|
||||
|
||||
let toolbarActions = [];
|
||||
_tasks.forEach(a => {
|
||||
let iconClassName = TaskRegistry.getOrCreateTaskIconClassName(a);
|
||||
toolbarActions.push(new ToolbarAction(a.id, a.title.toString(), iconClassName, this.runAction, this, this.logService));
|
||||
});
|
||||
|
||||
let content: ITaskbarContent[] = [];
|
||||
toolbarActions.forEach(a => {
|
||||
content.push({ action: a });
|
||||
});
|
||||
|
||||
if (content.length > 0) {
|
||||
let separator: HTMLElement = Taskbar.createTaskbarSeparator();
|
||||
content.push({ element: separator });
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
private runAction(id: string): Promise<void> {
|
||||
return this.commandService.executeCommand(id, this.connectionManagementService.connectionInfo.connectionProfile);
|
||||
}
|
||||
|
||||
private createActionItemProvider(action: Action): IActionViewItem {
|
||||
// Create ActionItem for actions contributed by extensions
|
||||
if (action instanceof MenuItemAction) {
|
||||
return new LabeledMenuItemActionItem(action, this.keybindingService, this.contextMenuService, this.notificationService);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private createTabs(homeWidgets: WidgetConfig[]) {
|
||||
@@ -140,6 +303,8 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig
|
||||
// Before separating tabs into pinned / shown, ensure that the home tab is always set up as expected
|
||||
allTabs = this.setAndRemoveHomeTab(allTabs, homeWidgets);
|
||||
|
||||
this.loadNewTabs(allTabs.filter((tab) => tab.group === homeTabGroupId));
|
||||
|
||||
// If preview features are disabled only show the home tab
|
||||
const extensionTabsEnabled = this.configurationService.getValue('workbench')['enablePreviewFeatures'];
|
||||
if (!extensionTabsEnabled) {
|
||||
@@ -149,36 +314,11 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig
|
||||
// Load tab setting configs
|
||||
this._tabSettingConfigs = this.dashboardService.getSettings<Array<TabSettingConfig>>([this.context, 'tabs'].join('.'));
|
||||
|
||||
const pinnedDashboardTabs: IDashboardTab[] = [];
|
||||
const alwaysShowTabs = allTabs.filter(tab => tab.alwaysShow);
|
||||
this.addCustomTabGroups(allTabs);
|
||||
this.addExtensionsTabGroup(allTabs);
|
||||
|
||||
this._tabSettingConfigs.forEach(config => {
|
||||
if (config.tabId && types.isBoolean(config.isPinned)) {
|
||||
const tab = find(allTabs, i => i.id === config.tabId);
|
||||
if (tab) {
|
||||
if (config.isPinned) {
|
||||
pinnedDashboardTabs.push(tab);
|
||||
} else {
|
||||
// overwrite always show if specify in user settings
|
||||
const index = firstIndex(alwaysShowTabs, i => i.id === tab.id);
|
||||
alwaysShowTabs.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
this.panelActions = [];
|
||||
|
||||
this.loadNewTabs(pinnedDashboardTabs);
|
||||
this.loadNewTabs(alwaysShowTabs);
|
||||
|
||||
// Set panel actions
|
||||
const openedTabs = [...pinnedDashboardTabs, ...alwaysShowTabs];
|
||||
if (extensionTabsEnabled) {
|
||||
const addNewTabAction = this.instantiationService.createInstance(AddFeatureTabAction, allTabs, openedTabs, this.dashboardService.getUnderlyingUri());
|
||||
this._tabsDispose.push(addNewTabAction);
|
||||
this.panelActions = [addNewTabAction];
|
||||
} else {
|
||||
this.panelActions = [];
|
||||
}
|
||||
this._cd.detectChanges();
|
||||
|
||||
this._tabsDispose.push(this.dashboardService.onPinUnpinTab(e => {
|
||||
@@ -196,18 +336,67 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the custom tab groups and their child tabs.
|
||||
* @param allTabs The available tabs
|
||||
*/
|
||||
private addCustomTabGroups(allTabs: IDashboardTab[]): void {
|
||||
dashboardRegistry.tabGroups.forEach((tabGroup) => {
|
||||
const tabs = allTabs.filter(tab => tab.group === tabGroup.id);
|
||||
if (tabs.length > 0) {
|
||||
this.addNewTab({
|
||||
id: tabGroup.id,
|
||||
provider: Constants.anyProviderName,
|
||||
originalConfig: [],
|
||||
publisher: undefined,
|
||||
title: tabGroup.title,
|
||||
context: this.context,
|
||||
type: 'group-header',
|
||||
editable: false,
|
||||
canClose: false,
|
||||
actions: []
|
||||
});
|
||||
this.loadNewTabs(tabs);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the "Extensions" tab group, tabs without a group will be added here.
|
||||
* @param allTabs The available tabs
|
||||
*/
|
||||
private addExtensionsTabGroup(allTabs: IDashboardTab[]): void {
|
||||
const tabs = allTabs.filter(tab => !tab.group);
|
||||
if (tabs.length > 0) {
|
||||
this.addNewTab({
|
||||
id: 'generalTabGroupHeader',
|
||||
provider: Constants.anyProviderName,
|
||||
originalConfig: [],
|
||||
publisher: undefined,
|
||||
title: nls.localize('dashboard.generalTabGroupHeader', "General"),
|
||||
context: this.context,
|
||||
type: 'group-header',
|
||||
editable: false,
|
||||
canClose: false,
|
||||
actions: []
|
||||
});
|
||||
this.loadNewTabs(tabs);
|
||||
}
|
||||
}
|
||||
|
||||
private setAndRemoveHomeTab(allTabs: IDashboardTab[], homeWidgets: WidgetConfig[]): IDashboardTab[] {
|
||||
const homeTabConfig: TabConfig = {
|
||||
id: 'homeTab',
|
||||
id: this.homeTabId,
|
||||
provider: Constants.anyProviderName,
|
||||
publisher: undefined,
|
||||
title: this.homeTabTitle,
|
||||
container: { 'widgets-container': homeWidgets },
|
||||
context: this.context,
|
||||
originalConfig: this._originalConfig,
|
||||
originalConfig: [],
|
||||
editable: true,
|
||||
canClose: false,
|
||||
actions: []
|
||||
actions: [],
|
||||
iconClass: 'home-tab-icon'
|
||||
};
|
||||
|
||||
const homeTabIndex = firstIndex(allTabs, (tab) => tab.isHomeTab === true);
|
||||
@@ -232,21 +421,11 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig
|
||||
private loadNewTabs(dashboardTabs: IDashboardTab[], openLastTab: boolean = false) {
|
||||
if (dashboardTabs && dashboardTabs.length > 0) {
|
||||
const selectedTabs = dashboardTabs.map(v => this.initTabComponents(v)).map(v => {
|
||||
const actions = [];
|
||||
const tabSettingConfig = find(this._tabSettingConfigs, i => i.tabId === v.id);
|
||||
let isPinned = false;
|
||||
if (tabSettingConfig) {
|
||||
isPinned = tabSettingConfig.isPinned;
|
||||
} else if (v.alwaysShow) {
|
||||
isPinned = true;
|
||||
}
|
||||
actions.push(this.instantiationService.createInstance(PinUnpinTabAction, v.id, this.dashboardService.getUnderlyingUri(), isPinned));
|
||||
|
||||
const config = v as TabConfig;
|
||||
config.context = this.context;
|
||||
config.editable = false;
|
||||
config.canClose = true;
|
||||
config.actions = actions;
|
||||
config.canClose = false;
|
||||
config.actions = [];
|
||||
this.addNewTab(config);
|
||||
return config;
|
||||
});
|
||||
@@ -261,12 +440,13 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig
|
||||
}
|
||||
}
|
||||
|
||||
private initTabComponents(value: IDashboardTab): { id: string; title: string; container: object; alwaysShow: boolean; } {
|
||||
private initTabComponents(value: IDashboardTab): { id: string; title: string; container: object; alwaysShow: boolean; iconClass?: string } {
|
||||
const containerResult = dashboardHelper.getDashboardContainer(value.container, this.logService);
|
||||
if (!containerResult.result) {
|
||||
return { id: value.id, title: value.title, container: { 'error-container': undefined }, alwaysShow: value.alwaysShow };
|
||||
return { id: value.id, title: value.title, container: { 'error-container': undefined }, alwaysShow: value.alwaysShow, iconClass: value.iconClass };
|
||||
}
|
||||
const key = Object.keys(containerResult.container)[0];
|
||||
this.tabContents.set(value.id, key);
|
||||
if (key === WIDGETS_CONTAINER || key === GRID_CONTAINER) {
|
||||
let configs = <WidgetConfig[]>values(containerResult.container)[0];
|
||||
this._configModifiers.forEach(cb => {
|
||||
@@ -275,14 +455,22 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig
|
||||
this._gridModifiers.forEach(cb => {
|
||||
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);
|
||||
}
|
||||
|
||||
if (key === WIDGETS_CONTAINER) {
|
||||
return { id: value.id, title: value.title, container: { 'widgets-container': configs }, alwaysShow: value.alwaysShow };
|
||||
return { id: value.id, title: value.title, container: { 'widgets-container': configs }, alwaysShow: value.alwaysShow, iconClass: value.iconClass };
|
||||
}
|
||||
else {
|
||||
return { id: value.id, title: value.title, container: { 'grid-container': configs }, alwaysShow: value.alwaysShow };
|
||||
return { id: value.id, title: value.title, container: { 'grid-container': configs }, alwaysShow: value.alwaysShow, iconClass: value.iconClass };
|
||||
}
|
||||
}
|
||||
return { id: value.id, title: value.title, container: containerResult.container, alwaysShow: value.alwaysShow };
|
||||
return { id: value.id, title: value.title, container: containerResult.container, alwaysShow: value.alwaysShow, iconClass: value.iconClass };
|
||||
}
|
||||
|
||||
protected getContentType(tab: TabConfig): string {
|
||||
@@ -292,6 +480,9 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig
|
||||
private addNewTab(tab: TabConfig): void {
|
||||
const existedTab = find(this.tabs, i => i.id === tab.id);
|
||||
if (!existedTab) {
|
||||
if (!tab.iconClass && tab.type !== 'group-header') {
|
||||
tab.iconClass = 'default-tab-icon';
|
||||
}
|
||||
this.tabs.push(tab);
|
||||
this._cd.detectChanges();
|
||||
}
|
||||
@@ -323,22 +514,25 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig
|
||||
this.init();
|
||||
} else {
|
||||
if (this._tabs) {
|
||||
this._tabs.forEach(tabContent => {
|
||||
tabContent.refresh();
|
||||
});
|
||||
const tab = this._tabs.find(t => t.id === this._tabName.get());
|
||||
if (tab) {
|
||||
tab.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enableEdit(): void {
|
||||
if (this._tabs) {
|
||||
this._tabs.forEach(tabContent => {
|
||||
tabContent.enableEdit();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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()) {
|
||||
this.showToolbar = true;
|
||||
this.createToolbar(this.toolbarContainer.nativeElement, tab.identifier);
|
||||
} else { // hide toolbar
|
||||
this.showToolbar = false;
|
||||
}
|
||||
|
||||
this._cd.detectChanges();
|
||||
const localtab = this._tabs.find(i => i.id === tab.identifier);
|
||||
this._editEnabled.fire(localtab.editable);
|
||||
@@ -350,4 +544,9 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig
|
||||
this.tabs.splice(index, 1);
|
||||
this.angularEventingService.sendAngularEvent(this.dashboardService.getUnderlyingUri(), AngularEventType.CLOSE_TAB, { id: tab.identifier });
|
||||
}
|
||||
|
||||
private updateTheme(theme: IColorTheme): void {
|
||||
const border = theme.getColor(DASHBOARD_BORDER);
|
||||
this.toolbarContainer.nativeElement.style.borderBottomColor = border.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,3 +23,43 @@ dashboard-page .dashboard-panel .tab-header .action-item .action-label.pin {
|
||||
dashboard-page .dashboard-panel .tab-header .action-item .action-label.close {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
dashboard-page .home-tab-icon {
|
||||
background-image: url("media/home.svg");
|
||||
}
|
||||
|
||||
.vs-dark dashboard-page .home-tab-icon,
|
||||
.hc-black dashboard-page .home-tab-icon {
|
||||
background-image: url("media/home_inverse.svg");
|
||||
}
|
||||
|
||||
dashboard-page .default-tab-icon {
|
||||
background-image: url("media/default.svg");
|
||||
}
|
||||
|
||||
.vs-dark dashboard-page .default-tab-icon,
|
||||
.hc-black dashboard-page .default-tab-icon {
|
||||
background-image: url("media/default_inverse.svg");
|
||||
}
|
||||
|
||||
dashboard-page .actions-container .action-item .action-label{
|
||||
padding-left: 20px;
|
||||
padding-right: 5px;
|
||||
background-size: 16px;
|
||||
background-position: left;
|
||||
}
|
||||
|
||||
dashboard-page .actions-container .taskbarSeparator {
|
||||
height: 14px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
dashboard-page .editor-toolbar {
|
||||
flex: 0 0 auto;
|
||||
flex-flow: row;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
padding: 3px 0;
|
||||
}
|
||||
|
||||
@@ -21,4 +21,4 @@ panel.dashboard-panel > .tabbedPanel > .title > .title-actions,
|
||||
panel.dashboard-panel > .tabbedPanel > .title > .monaco-scrollable-element > .tabList .tab-header {
|
||||
box-sizing: border-box;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,19 +4,19 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./dashboardPanel';
|
||||
|
||||
import { registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
|
||||
import { registerThemingParticipant, IColorTheme, ICssStyleCollector, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService';
|
||||
import {
|
||||
TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, TAB_ACTIVE_BORDER, TAB_INACTIVE_BACKGROUND,
|
||||
TAB_INACTIVE_FOREGROUND, EDITOR_GROUP_HEADER_TABS_BACKGROUND, TAB_BORDER, EDITOR_GROUP_BORDER
|
||||
TAB_INACTIVE_FOREGROUND, EDITOR_GROUP_HEADER_TABS_BACKGROUND, TAB_BORDER, EDITOR_GROUP_BORDER, DASHBOARD_TAB_ACTIVE_BACKGROUND, DASHBOARD_BORDER
|
||||
} from 'vs/workbench/common/theme';
|
||||
import { activeContrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
|
||||
registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => {
|
||||
|
||||
// Title Active
|
||||
const tabActiveBackground = theme.getColor(TAB_ACTIVE_BACKGROUND);
|
||||
const tabActiveForeground = theme.getColor(TAB_ACTIVE_FOREGROUND);
|
||||
let tabActiveBackgroundVertical = theme.getColor(DASHBOARD_TAB_ACTIVE_BACKGROUND);
|
||||
|
||||
if (tabActiveBackground || tabActiveForeground) {
|
||||
collector.addRule(`
|
||||
panel.dashboard-panel > .tabbedPanel > .title .tabList .tab:hover .tabLabel,
|
||||
@@ -25,9 +25,12 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) =
|
||||
border-bottom: 0px solid;
|
||||
}
|
||||
|
||||
panel.dashboard-panel > .tabbedPanel > .title .tabList .tab-header.active {
|
||||
panel.dashboard-panel > .tabbedPanel.vertical > .title .tabList .tab-header.active {
|
||||
background-color: ${tabActiveBackgroundVertical};
|
||||
}
|
||||
|
||||
panel.dashboard-panel > .tabbedPanel.horizontal > .title .tabList .tab-header.active {
|
||||
background-color: ${tabActiveBackground};
|
||||
outline-color: ${tabActiveBackground};
|
||||
}
|
||||
|
||||
panel.dashboard-panel > .tabbedPanel.horizontal > .title .tabList .tab-header.active {
|
||||
@@ -40,7 +43,7 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) =
|
||||
`);
|
||||
}
|
||||
|
||||
const activeTabBorderColor = theme.getColor(TAB_ACTIVE_BORDER);
|
||||
const activeTabBorderColor = theme.type === HIGH_CONTRAST ? theme.getColor(activeContrastBorder) : theme.getColor(TAB_ACTIVE_BORDER);
|
||||
if (activeTabBorderColor) {
|
||||
collector.addRule(`
|
||||
panel.dashboard-panel > .tabbedPanel > .title .tabList .tab-header.active {
|
||||
@@ -54,11 +57,10 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) =
|
||||
const tabInactiveForeground = theme.getColor(TAB_INACTIVE_FOREGROUND);
|
||||
if (tabInactiveBackground || tabInactiveForeground) {
|
||||
collector.addRule(`
|
||||
panel.dashboard-panel > .tabbedPanel > .title .tabList .tab .tabLabel {
|
||||
panel.dashboard-panel > .tabbedPanel.horizontal > .title .tabList .tab .tabLabel {
|
||||
color: ${tabInactiveForeground};
|
||||
}
|
||||
|
||||
panel.dashboard-panel > .tabbedPanel > .title .tabList .tab-header {
|
||||
panel.dashboard-panel > .tabbedPanel.horizontal > .title .tabList .tab-header {
|
||||
background-color: ${tabInactiveBackground};
|
||||
}
|
||||
`);
|
||||
@@ -68,7 +70,7 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) =
|
||||
const panelTitleBackground = theme.getColor(EDITOR_GROUP_HEADER_TABS_BACKGROUND);
|
||||
if (panelTitleBackground) {
|
||||
collector.addRule(`
|
||||
panel.dashboard-panel > .tabbedPanel > .title {
|
||||
panel.dashboard-panel > .tabbedPanel.horizontal > .title {
|
||||
background-color: ${panelTitleBackground};
|
||||
}
|
||||
`);
|
||||
@@ -78,7 +80,7 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) =
|
||||
const tabBorder = theme.getColor(TAB_BORDER);
|
||||
if (tabBorder) {
|
||||
collector.addRule(`
|
||||
panel.dashboard-panel > .tabbedPanel > .title .tabList .tab-header {
|
||||
panel.dashboard-panel > .tabbedPanel.horizontal > .title .tabList .tab-header {
|
||||
border-right-color: ${tabBorder};
|
||||
border-bottom-color: ${tabBorder};
|
||||
}
|
||||
@@ -89,7 +91,7 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) =
|
||||
const outline = theme.getColor(activeContrastBorder);
|
||||
if (outline) {
|
||||
collector.addRule(`
|
||||
panel.dashboard-panel > .tabbedPanel > .title {
|
||||
panel.dashboard-panel > .tabbedPanel.horizontal > .title {
|
||||
border-bottom-color: ${tabBorder};
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
@@ -106,10 +108,19 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) =
|
||||
const divider = theme.getColor(EDITOR_GROUP_BORDER);
|
||||
if (divider) {
|
||||
collector.addRule(`
|
||||
panel.dashboard-panel > .tabbedPanel > .title .tabList .tab-header {
|
||||
panel.dashboard-panel > .tabbedPanel.horizontal > .title .tabList .tab-header {
|
||||
border-right-width: 1px;
|
||||
border-right-style: solid;
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
const sideBorder = theme.getColor(DASHBOARD_BORDER);
|
||||
if (divider) {
|
||||
collector.addRule(`panel.dashboard-panel > .tabbedPanel.vertical > .title > .tabContainer {
|
||||
border-right-width: 1px;
|
||||
border-right-style: solid;
|
||||
border-right-color: ${sideBorder};
|
||||
}`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -9,12 +9,14 @@ import { localize } from 'vs/nls';
|
||||
import * as types from 'vs/base/common/types';
|
||||
|
||||
import * as Constants from 'sql/platform/connection/common/constants';
|
||||
import { registerTab } from 'sql/workbench/contrib/dashboard/browser/dashboardRegistry';
|
||||
import { registerTab, registerTabGroup } from 'sql/workbench/contrib/dashboard/browser/dashboardRegistry';
|
||||
import { generateContainerTypeSchemaProperties } from 'sql/platform/dashboard/common/dashboardContainerRegistry';
|
||||
import { NAV_SECTION, validateNavSectionContributionAndRegisterIcon } from 'sql/workbench/contrib/dashboard/browser/containers/dashboardNavSection.contribution';
|
||||
import { WIDGETS_CONTAINER, validateWidgetContainerContribution } from 'sql/workbench/contrib/dashboard/browser/containers/dashboardWidgetContainer.contribution';
|
||||
import { GRID_CONTAINER, validateGridContainerContribution } from 'sql/workbench/contrib/dashboard/browser/containers/dashboardGridContainer.contribution';
|
||||
import { values } from 'vs/base/common/collections';
|
||||
import { IUserFriendlyIcon } from 'sql/workbench/contrib/dashboard/browser/core/dashboardWidget';
|
||||
import { isValidIcon, createCSSRuleForIcon } from 'sql/workbench/contrib/dashboard/browser/dashboardIconUtil';
|
||||
|
||||
export interface IDashboardTabContrib {
|
||||
id: string;
|
||||
@@ -25,6 +27,13 @@ export interface IDashboardTabContrib {
|
||||
description?: string;
|
||||
alwaysShow?: boolean;
|
||||
isHomeTab?: boolean;
|
||||
group?: string;
|
||||
icon?: IUserFriendlyIcon;
|
||||
}
|
||||
|
||||
export interface IDashboardTabGroupContrib {
|
||||
id: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
const tabSchema: IJSONSchema = {
|
||||
@@ -63,6 +72,29 @@ const tabSchema: IJSONSchema = {
|
||||
isHomeTab: {
|
||||
description: localize('azdata.extension.contributes.dashboard.tab.isHomeTab', "Whether or not this tab should be used as the Home tab for a connection type."),
|
||||
type: 'boolean'
|
||||
},
|
||||
group: {
|
||||
description: localize('azdata.extension.contributes.dashboard.tab.group', "The unique identifier of the group this tab belongs to, value for home group: home."),
|
||||
type: 'string'
|
||||
},
|
||||
icon: {
|
||||
description: localize('dazdata.extension.contributes.dashboard.tab.icon', "(Optional) Icon which is used to represent this tab in the UI. Either a file path or a themeable configuration"),
|
||||
anyOf: [{
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
light: {
|
||||
description: localize('azdata.extension.contributes.dashboard.tab.icon.light', "Icon path when a light theme is used"),
|
||||
type: 'string'
|
||||
},
|
||||
dark: {
|
||||
description: localize('azdata.extension.contributes.dashboard.tab.icon.dark', "Icon path when a dark theme is used"),
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -80,8 +112,8 @@ const tabContributionSchema: IJSONSchema = {
|
||||
|
||||
ExtensionsRegistry.registerExtensionPoint<IDashboardTabContrib | IDashboardTabContrib[]>({ extensionPoint: 'dashboard.tabs', jsonSchema: tabContributionSchema }).setHandler(extensions => {
|
||||
|
||||
function handleCommand(tab: IDashboardTabContrib, extension: IExtensionPointUser<any>) {
|
||||
let { description, container, provider, title, when, id, alwaysShow, isHomeTab } = tab;
|
||||
function handleTab(tab: IDashboardTabContrib, extension: IExtensionPointUser<any>) {
|
||||
let { description, container, provider, title, when, id, alwaysShow, isHomeTab, group, icon } = tab;
|
||||
|
||||
// If always show is not specified, set it to true by default.
|
||||
if (!types.isBoolean(alwaysShow)) {
|
||||
@@ -130,8 +162,13 @@ ExtensionsRegistry.registerExtensionPoint<IDashboardTabContrib | IDashboardTabCo
|
||||
break;
|
||||
}
|
||||
|
||||
let iconClass = undefined;
|
||||
if (isValidIcon(icon, extension)) {
|
||||
iconClass = createCSSRuleForIcon(icon, extension);
|
||||
}
|
||||
|
||||
if (result) {
|
||||
registerTab({ description, title, container, provider, when, id, alwaysShow, publisher, isHomeTab });
|
||||
registerTab({ description, title, container, provider, when, id, alwaysShow, publisher, isHomeTab, group, iconClass });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,10 +176,64 @@ ExtensionsRegistry.registerExtensionPoint<IDashboardTabContrib | IDashboardTabCo
|
||||
const { value } = extension;
|
||||
if (Array.isArray<IDashboardTabContrib>(value)) {
|
||||
for (const command of value) {
|
||||
handleCommand(command, extension);
|
||||
handleTab(command, extension);
|
||||
}
|
||||
} else {
|
||||
handleCommand(value, extension);
|
||||
handleTab(value, extension);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const tabGroupSchema: IJSONSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: localize('azdata.extension.contributes.dashboard.tabGroup.id', "Unique identifier for this tab group.")
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
description: localize('azdata.extension.contributes.dashboard.tabGroup.title', "Title of the tab group.")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const tabGroupContributionSchema: IJSONSchema = {
|
||||
description: localize('azdata.extension.contributes.tabGroups', "Contributes a single or multiple tab groups for users to add to their dashboard."),
|
||||
oneOf: [
|
||||
tabGroupSchema,
|
||||
{
|
||||
type: 'array',
|
||||
items: tabGroupSchema
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
ExtensionsRegistry.registerExtensionPoint<IDashboardTabContrib | IDashboardTabContrib[]>({ extensionPoint: 'dashboard.tabGroups', jsonSchema: tabGroupContributionSchema }).setHandler(extensions => {
|
||||
|
||||
function handleTabGroup(tabgroup: IDashboardTabGroupContrib, extension: IExtensionPointUser<any>) {
|
||||
let { id, title } = tabgroup;
|
||||
|
||||
if (!id) {
|
||||
extension.collector.error(localize('dashboardTabGroup.contribution.noIdError', "No id specified for tab group."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!title) {
|
||||
extension.collector.error(localize('dashboardTabGroup.contribution.noTitleError', "No title specified for tab group."));
|
||||
return;
|
||||
}
|
||||
registerTabGroup({ id, title });
|
||||
}
|
||||
|
||||
for (const extension of extensions) {
|
||||
const { value } = extension;
|
||||
if (Array.isArray<IDashboardTabGroupContrib>(value)) {
|
||||
for (const command of value) {
|
||||
handleTabGroup(command, extension);
|
||||
}
|
||||
} else {
|
||||
handleTabGroup(value, extension);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@ 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';
|
||||
import { TabType } from 'sql/base/browser/ui/panel/tab.component';
|
||||
import { IDashboardTab } from 'sql/workbench/services/dashboard/browser/common/interfaces';
|
||||
|
||||
export interface IDashboardWidget {
|
||||
@@ -41,9 +42,10 @@ export interface TabConfig extends IDashboardTab {
|
||||
editable: boolean;
|
||||
canClose: boolean;
|
||||
actions?: Array<Action>;
|
||||
iconClass?: string;
|
||||
type?: TabType;
|
||||
}
|
||||
|
||||
|
||||
export type IUserFriendlyIcon = string | { light: string; dark: string; };
|
||||
|
||||
export interface NavSectionConfig {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2048 2048" width="16" height="16">
|
||||
<path d="M1792 569v1038l-832 417-832-417V569l832-417 832 417zM960 296L335 608l625 312 625-312-625-312zM256 1528l640 321v-817L256 711v817zm1408 0V711l-640 321v817l640-321z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 271 B |
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M14,4.4v8.1l-6.5,3.3L1,12.6V4.4l6.5-3.3L14,4.4z M7.5,2.3L2.6,4.8l4.9,2.4l4.9-2.4L7.5,2.3z M2,11.9l5,2.5V8.1
|
||||
L2,5.6V11.9z M13,11.9V5.6L8,8.1v6.4L13,11.9z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 584 B |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg enable-background="new 0 0 50 50" height="50px" id="Layer_1" version="1.1" viewBox="0 0 50 50" width="50px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><rect fill="none" height="50" width="50"/><polyline fill="none" points="44,21 44,49 6,49 6,21 " stroke="#000000" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2"/><polyline fill="none" points="19,49 19,28 31,28 31,49 " stroke="#000000" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2"/><polygon points="35,5 35,8.016 37,10.094 37,7 39,7 39,12.203 41,14.266 41,5 "/><polyline fill="none" points=" 1.11,25.942 25,1.053 48.89,25.943 " stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2"/></svg>
|
||||
|
After Width: | Height: | Size: 910 B |
@@ -0,0 +1,15 @@
|
||||
<svg width="50" height="50" xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<g>
|
||||
<title>background</title>
|
||||
<rect fill="none" id="canvas_background" height="402" width="582" y="-1" x="-1"/>
|
||||
</g>
|
||||
<g>
|
||||
<title>Layer 1</title>
|
||||
<rect id="svg_1" width="50" height="50" fill="none"/>
|
||||
<polyline id="svg_2" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke="#ffffff" points="44,21 44,49 6,49 6,21 " fill="none"/>
|
||||
<polyline id="svg_3" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke="#ffffff" points="18.9292186871171,49 18.9292186871171,28 30.9292186871171,28 30.9292186871171,49 " fill="none"/>
|
||||
<polygon fill="#ffffff" stroke="#000000" stroke-opacity="0" id="svg_4" points="35,5 35,8.016 37,10.094 37,7 39,7 39,12.203 41,14.266 41,5 "/>
|
||||
<polyline id="svg_5" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round" stroke-linecap="round" stroke="#ffffff" points=" 1.11,25.942 25,1.053 48.89,25.943 " fill="none"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 975 B |
@@ -9,8 +9,6 @@
|
||||
<div style="flex: 1 1 auto">
|
||||
<breadcrumb></breadcrumb>
|
||||
</div>
|
||||
<div style="flex: 0 0 auto" #actionBar>
|
||||
</div>
|
||||
</div>
|
||||
<div style="flex: 1 1 auto; position: relative">
|
||||
<router-outlet (activate)="onActivate($event)"></router-outlet>
|
||||
|
||||
@@ -2,23 +2,17 @@
|
||||
* 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!./dashboard';
|
||||
|
||||
import { OnInit, Component, Inject, forwardRef, ElementRef, ViewChild } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { CommonServiceInterface } from 'sql/workbench/services/bootstrap/browser/commonServiceInterface.service';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import * as Utils from 'sql/platform/connection/common/utils';
|
||||
import { RefreshWidgetAction, EditDashboardAction } from 'sql/workbench/contrib/dashboard/browser/core/actions';
|
||||
import { DashboardPage } from 'sql/workbench/contrib/dashboard/browser/core/dashboardPage.component';
|
||||
import { AngularDisposable } from 'sql/base/browser/lifecycle';
|
||||
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import * as themeColors from 'vs/workbench/common/theme';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IColorTheme } from 'vs/platform/theme/common/themeService';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
|
||||
@@ -32,10 +26,6 @@ export class DashboardComponent extends AngularDisposable implements OnInit {
|
||||
private _currentPage: DashboardPage;
|
||||
|
||||
@ViewChild('header', { read: ElementRef }) private header: ElementRef;
|
||||
@ViewChild('actionBar', { read: ElementRef }) private actionbarContainer: ElementRef;
|
||||
private actionbar: ActionBar;
|
||||
private editAction: EditDashboardAction;
|
||||
private editDisposable: IDisposable;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) private _bootstrapService: CommonServiceInterface,
|
||||
@@ -49,16 +39,6 @@ export class DashboardComponent extends AngularDisposable implements OnInit {
|
||||
this._register(this.themeService.onDidColorThemeChange(this.updateTheme, this));
|
||||
this.updateTheme(this.themeService.getColorTheme());
|
||||
const profile: IConnectionProfile = this._bootstrapService.getOriginalConnectionProfile();
|
||||
this.actionbar = new ActionBar(this.actionbarContainer.nativeElement);
|
||||
this.actionbar.push(new RefreshWidgetAction(this.refresh, this), {
|
||||
icon: true,
|
||||
label: false,
|
||||
});
|
||||
this.editAction = new EditDashboardAction(this.edit, this);
|
||||
this.actionbar.push(this.editAction, {
|
||||
icon: true,
|
||||
label: false,
|
||||
});
|
||||
if (profile && (!profile.databaseName || Utils.isMaster(profile))) {
|
||||
// Route to the server page as this is the default database
|
||||
this._router.navigate(['server-dashboard']).catch(onUnexpectedError);
|
||||
@@ -73,11 +53,7 @@ export class DashboardComponent extends AngularDisposable implements OnInit {
|
||||
}
|
||||
|
||||
onActivate(page: DashboardPage) {
|
||||
if (this.editDisposable) {
|
||||
this.editDisposable.dispose();
|
||||
}
|
||||
this._currentPage = page;
|
||||
this.editDisposable = page.editEnabled(e => this.editEnabled = e, this);
|
||||
}
|
||||
|
||||
refresh(): void {
|
||||
@@ -85,12 +61,4 @@ export class DashboardComponent extends AngularDisposable implements OnInit {
|
||||
this._currentPage.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
edit(): void {
|
||||
this._currentPage.enableEdit();
|
||||
}
|
||||
|
||||
set editEnabled(val: boolean) {
|
||||
this.editAction.enabled = val;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.dashboardEditor .header .monaco-action-bar .action-label {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.dashboardEditor .header .monaco-action-bar .action-item {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.dashboardEditor .monaco-action-bar {
|
||||
overflow: visible;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IUserFriendlyIcon } from 'sql/workbench/contrib/dashboard/browser/core/dashboardWidget';
|
||||
import { asCSSUrl, createCSSRule } from 'vs/base/browser/dom';
|
||||
import { IdGenerator } from 'vs/base/common/idGenerator';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import * as nls from 'vs/nls';
|
||||
import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||
|
||||
const ids = new IdGenerator('contrib-dashboard-icon-');
|
||||
export function createCSSRuleForIcon(icon: IUserFriendlyIcon, extension: IExtensionPointUser<any>): string {
|
||||
let iconClass: string;
|
||||
if (icon) {
|
||||
iconClass = ids.nextId();
|
||||
if (typeof icon === 'string') {
|
||||
const path = resources.joinPath(extension.description.extensionLocation, icon);
|
||||
createCSSRule(`.codicon.${iconClass}`, `background-image: ${asCSSUrl(path)}`);
|
||||
} else {
|
||||
const light = resources.joinPath(extension.description.extensionLocation, icon.light);
|
||||
const dark = resources.joinPath(extension.description.extensionLocation, icon.dark);
|
||||
createCSSRule(`.codicon.${iconClass}`, `background-image: ${asCSSUrl(light)}`);
|
||||
createCSSRule(`.vs-dark .codicon.${iconClass}, .hc-black .codicon.${iconClass}`, `background-image: ${asCSSUrl(dark)}`);
|
||||
}
|
||||
}
|
||||
return iconClass;
|
||||
}
|
||||
|
||||
export function isValidIcon(icon: IUserFriendlyIcon, extension: IExtensionPointUser<any>): boolean {
|
||||
if (typeof icon === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
if (typeof icon === 'string') {
|
||||
return true;
|
||||
} else if (typeof icon.dark === 'string' && typeof icon.light === 'string') {
|
||||
return true;
|
||||
}
|
||||
extension.collector.error(nls.localize('opticon', "property `icon` can be omitted or must be either a string or a literal like `{dark, light}`"));
|
||||
return false;
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import { DATABASE_DASHBOARD_TABS } from 'sql/workbench/contrib/dashboard/browser
|
||||
import { SERVER_DASHBOARD_TABS } from 'sql/workbench/contrib/dashboard/browser/pages/serverDashboardPage.contribution';
|
||||
import { DASHBOARD_CONFIG_ID, DASHBOARD_TABS_KEY_PROPERTY } from 'sql/workbench/contrib/dashboard/browser/pages/dashboardPageContribution';
|
||||
import { find } from 'vs/base/common/arrays';
|
||||
import { IDashboardTab } from 'sql/workbench/services/dashboard/browser/common/interfaces';
|
||||
import { IDashboardTab, IDashboardTabGroup } from 'sql/workbench/services/dashboard/browser/common/interfaces';
|
||||
|
||||
export const Extensions = {
|
||||
DashboardContributions: 'dashboard.contributions'
|
||||
@@ -24,12 +24,15 @@ export interface IDashboardRegistry {
|
||||
registerDashboardProvider(id: string, properties: ProviderProperties): void;
|
||||
getProperties(id: string): ProviderProperties;
|
||||
registerTab(tab: IDashboardTab): void;
|
||||
registerTabGroup(tabGroup: IDashboardTabGroup): void;
|
||||
tabs: Array<IDashboardTab>;
|
||||
tabGroups: Array<IDashboardTabGroup>;
|
||||
}
|
||||
|
||||
class DashboardRegistry implements IDashboardRegistry {
|
||||
private _properties = new Map<string, ProviderProperties>();
|
||||
private _tabs = new Array<IDashboardTab>();
|
||||
private _tabGroups = new Array<IDashboardTabGroup>();
|
||||
private _configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtension.Configuration);
|
||||
|
||||
/**
|
||||
@@ -61,9 +64,19 @@ class DashboardRegistry implements IDashboardRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
registerTabGroup(tabGroup: IDashboardTabGroup): void {
|
||||
if (this.tabGroups.findIndex(group => group.id === tabGroup.id) === -1) {
|
||||
this.tabGroups.push(tabGroup);
|
||||
}
|
||||
}
|
||||
|
||||
public get tabs(): Array<IDashboardTab> {
|
||||
return this._tabs;
|
||||
}
|
||||
|
||||
public get tabGroups(): Array<IDashboardTabGroup> {
|
||||
return this._tabGroups;
|
||||
}
|
||||
}
|
||||
|
||||
const dashboardRegistry = new DashboardRegistry();
|
||||
@@ -73,6 +86,10 @@ export function registerTab(tab: IDashboardTab): void {
|
||||
dashboardRegistry.registerTab(tab);
|
||||
}
|
||||
|
||||
export function registerTabGroup(tabGroup: IDashboardTabGroup): void {
|
||||
dashboardRegistry.registerTabGroup(tabGroup);
|
||||
}
|
||||
|
||||
const dashboardPropertiesPropertyContrib: IJSONSchema = {
|
||||
description: nls.localize('dashboard.properties.property', "Defines a property to show on the dashboard"),
|
||||
type: 'object',
|
||||
|
||||
@@ -144,4 +144,4 @@ export function generateDashboardTabSchema(type?: 'database' | 'server'): IJSONS
|
||||
}
|
||||
|
||||
export const DASHBOARD_CONFIG_ID = 'Dashboard';
|
||||
export const DASHBOARD_TABS_KEY_PROPERTY = 'tabId';
|
||||
export const DASHBOARD_TABS_KEY_PROPERTY = 'tabId';
|
||||
|
||||
@@ -15,10 +15,15 @@ import { IAngularEventingService } from 'sql/platform/angularEventing/browser/an
|
||||
|
||||
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
||||
import * as nls from 'vs/nls';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IMenuService } from 'vs/platform/actions/common/actions';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
|
||||
export class DatabaseDashboardPage extends DashboardPage implements OnInit {
|
||||
protected propertiesWidget: WidgetConfig = {
|
||||
@@ -42,13 +47,18 @@ export class DatabaseDashboardPage extends DashboardPage implements OnInit {
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) dashboardService: DashboardServiceInterface,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) _cd: ChangeDetectorRef,
|
||||
@Inject(forwardRef(() => ElementRef)) el: ElementRef,
|
||||
@Inject(IInstantiationService) instantiationService: IInstantiationService,
|
||||
@Inject(INotificationService) notificationService: INotificationService,
|
||||
@Inject(IAngularEventingService) angularEventingService: IAngularEventingService,
|
||||
@Inject(IConfigurationService) configurationService: IConfigurationService,
|
||||
@Inject(ILogService) logService: ILogService
|
||||
@Inject(ILogService) logService: ILogService,
|
||||
@Inject(ICommandService) commandService: ICommandService,
|
||||
@Inject(IContextKeyService) contextKeyService: IContextKeyService,
|
||||
@Inject(IMenuService) menuService: IMenuService,
|
||||
@Inject(IKeybindingService) keybindingService: IKeybindingService,
|
||||
@Inject(IContextMenuService) contextMenuService: IContextMenuService,
|
||||
@Inject(IWorkbenchThemeService) themeService: IWorkbenchThemeService
|
||||
) {
|
||||
super(dashboardService, el, _cd, instantiationService, notificationService, angularEventingService, configurationService, logService);
|
||||
super(dashboardService, el, _cd, notificationService, angularEventingService, configurationService, logService, commandService, contextKeyService, menuService, keybindingService, contextMenuService, themeService);
|
||||
this._register(dashboardService.onUpdatePage(() => {
|
||||
this.refresh(true);
|
||||
this._cd.detectChanges();
|
||||
@@ -58,5 +68,6 @@ export class DatabaseDashboardPage extends DashboardPage implements OnInit {
|
||||
ngOnInit() {
|
||||
this.init();
|
||||
this._breadcrumbService.setBreadcrumbs(BreadcrumbClass.DatabasePage);
|
||||
super.ngAfterViewInit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,15 +99,14 @@ export const databaseDashboardSettingSchema: IJSONSchema = {
|
||||
'newQuery',
|
||||
'mssqlCluster.task.newNotebook',
|
||||
{ name: 'backup', when: '!mssql:iscloud && mssql:engineedition != 11' },
|
||||
{ name: 'restore', when: '!mssql:iscloud && mssql:engineedition != 11' },
|
||||
'configureDashboard'
|
||||
{ name: 'restore', when: '!mssql:iscloud && mssql:engineedition != 11' }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Search',
|
||||
name: nls.localize('objectsWidgetTitle', "Search"),
|
||||
gridItemConfig: {
|
||||
sizex: 1,
|
||||
sizex: 3,
|
||||
sizey: 2
|
||||
},
|
||||
widget: {
|
||||
|
||||
@@ -16,11 +16,16 @@ import { IAngularEventingService } from 'sql/platform/angularEventing/browser/an
|
||||
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
||||
import * as nls from 'vs/nls';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IMenuService } from 'vs/platform/actions/common/actions';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
|
||||
export class ServerDashboardPage extends DashboardPage implements OnInit {
|
||||
protected propertiesWidget: WidgetConfig = {
|
||||
@@ -45,13 +50,18 @@ export class ServerDashboardPage extends DashboardPage implements OnInit {
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) dashboardService: DashboardServiceInterface,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) _cd: ChangeDetectorRef,
|
||||
@Inject(forwardRef(() => ElementRef)) el: ElementRef,
|
||||
@Inject(IInstantiationService) instantiationService: IInstantiationService,
|
||||
@Inject(INotificationService) notificationService: INotificationService,
|
||||
@Inject(IAngularEventingService) angularEventingService: IAngularEventingService,
|
||||
@Inject(IConfigurationService) configurationService: IConfigurationService,
|
||||
@Inject(ILogService) logService: ILogService
|
||||
@Inject(ILogService) logService: ILogService,
|
||||
@Inject(ICommandService) commandService: ICommandService,
|
||||
@Inject(IContextKeyService) contextKeyService: IContextKeyService,
|
||||
@Inject(IMenuService) menuService: IMenuService,
|
||||
@Inject(IKeybindingService) keybindingService: IKeybindingService,
|
||||
@Inject(IContextMenuService) contextMenuService: IContextMenuService,
|
||||
@Inject(IWorkbenchThemeService) themeService: IWorkbenchThemeService
|
||||
) {
|
||||
super(dashboardService, el, _cd, instantiationService, notificationService, angularEventingService, configurationService, logService);
|
||||
super(dashboardService, el, _cd, notificationService, angularEventingService, configurationService, logService, commandService, contextKeyService, menuService, keybindingService, contextMenuService, themeService);
|
||||
|
||||
// special-case handling for MSSQL data provider
|
||||
const connInfo = this.dashboardService.connectionManagementService.connectionInfo;
|
||||
|
||||
@@ -77,7 +77,7 @@ const defaultVal = [
|
||||
{
|
||||
name: 'Tasks',
|
||||
widget: {
|
||||
'tasks-widget': ['newQuery', 'mssqlCluster.task.newNotebook', { name: 'restore', when: '!mssql:iscloud && mssql:engineedition != 11' }, 'configureDashboard']
|
||||
'tasks-widget': ['newQuery', 'mssqlCluster.task.newNotebook', { name: 'restore', when: '!mssql:iscloud && mssql:engineedition != 11' }]
|
||||
},
|
||||
gridItemConfig: {
|
||||
sizex: 1,
|
||||
@@ -85,7 +85,7 @@ const defaultVal = [
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Search',
|
||||
name: nls.localize('databasesWidgetTitle', "Search"),
|
||||
gridItemConfig: {
|
||||
sizex: 1,
|
||||
sizey: 2
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
<div #parent style="position: absolute; height: 100%; width: 100%;">
|
||||
<div [style.margin-right.px]="_clipped ? 30 : 0" [style.width]="_clipped ? 94 + '%' : '100%'" style="overflow: hidden">
|
||||
<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">
|
||||
<span style="margin-left: 10px; display: inline-block;">
|
||||
|
||||
@@ -18,6 +18,9 @@ import * as nls from 'vs/nls';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { subscriptionToDisposable } from 'sql/base/browser/lifecycle';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { DASHBOARD_BORDER } from 'vs/workbench/common/theme';
|
||||
import { IColorTheme } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
export interface PropertiesConfig {
|
||||
properties: Array<Property>;
|
||||
@@ -69,13 +72,15 @@ export class PropertiesWidgetComponent extends DashboardWidget implements IDashb
|
||||
|
||||
@ViewChild('child', { read: ElementRef }) private _child: ElementRef;
|
||||
@ViewChild('parent', { read: ElementRef }) private _parent: ElementRef;
|
||||
@ViewChild('container', { read: ElementRef }) private _container: ElementRef;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) private _bootstrap: CommonServiceInterface,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
|
||||
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
|
||||
@Inject(WIDGET_CONFIG) protected _config: WidgetConfig,
|
||||
@Inject(ILogService) private logService: ILogService
|
||||
@Inject(ILogService) private logService: ILogService,
|
||||
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService
|
||||
) {
|
||||
super();
|
||||
this.init();
|
||||
@@ -85,6 +90,14 @@ export class PropertiesWidgetComponent extends DashboardWidget implements IDashb
|
||||
this._hasInit = true;
|
||||
this._register(addDisposableListener(window, EventType.RESIZE, () => this.handleClipping()));
|
||||
this._changeRef.detectChanges();
|
||||
|
||||
this._register(this.themeService.onDidColorThemeChange((event: IColorTheme) => {
|
||||
this.updateTheme(event);
|
||||
}));
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.updateTheme(this.themeService.getColorTheme());
|
||||
}
|
||||
|
||||
public refresh(): void {
|
||||
@@ -265,4 +278,9 @@ export class PropertiesWidgetComponent extends DashboardWidget implements IDashb
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
private updateTheme(theme: IColorTheme): void {
|
||||
const border = theme.getColor(DASHBOARD_BORDER);
|
||||
this._container.nativeElement.style.borderBottom = '1px solid ' + border.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ suite('Dashboard Properties Widget Tests', () => {
|
||||
}
|
||||
};
|
||||
|
||||
let testComponent = new PropertiesWidgetComponent(dashboardService.object, new TestChangeDetectorRef(), undefined, widgetConfig, testLogService);
|
||||
let testComponent = new PropertiesWidgetComponent(dashboardService.object, new TestChangeDetectorRef(), undefined, widgetConfig, testLogService, undefined);
|
||||
|
||||
return new Promise(resolve => {
|
||||
// because config parsing is done async we need to put our asserts on the thread stack
|
||||
|
||||
@@ -47,7 +47,7 @@ export class AgentViewComponent {
|
||||
|
||||
public readonly panelOpt: IPanelOptions = {
|
||||
showTabsWhenOne: true,
|
||||
layout: NavigationBarLayout.vertical,
|
||||
layout: NavigationBarLayout.horizontal,
|
||||
showIcon: true
|
||||
};
|
||||
|
||||
|
||||
@@ -30,9 +30,9 @@ import { registerComponentType } from 'sql/platform/dashboard/browser/modelCompo
|
||||
import HyperlinkComponent from 'sql/workbench/browser/modelComponents/hyperlink.component';
|
||||
import SplitViewContainer from 'sql/workbench/browser/modelComponents/splitviewContainer.component';
|
||||
import RadioCardGroup from 'sql/workbench/browser/modelComponents/radioCardGroup.component';
|
||||
import TabbedPanelComponent from 'sql/workbench/browser/modelComponents/tabbedPanel.component';
|
||||
import SeparatorComponent from 'sql/workbench/browser/modelComponents/separator.component';
|
||||
import { ModelComponentTypes } from 'sql/platform/dashboard/browser/interfaces';
|
||||
|
||||
export const DIV_CONTAINER = 'div-container';
|
||||
registerComponentType(DIV_CONTAINER, ModelComponentTypes.DivContainer, DivContainer);
|
||||
|
||||
@@ -112,5 +112,8 @@ registerComponentType(HYPERLINK_COMPONENT, ModelComponentTypes.Hyperlink, Hyperl
|
||||
export const RADIOCARDGROUP_COMPONENT = 'radiocardgroup-component';
|
||||
registerComponentType(RADIOCARDGROUP_COMPONENT, ModelComponentTypes.RadioCardGroup, RadioCardGroup);
|
||||
|
||||
export const TABBEDPANEL_COMPONENT = 'tabbedpanel-component';
|
||||
registerComponentType(TABBEDPANEL_COMPONENT, ModelComponentTypes.TabbedPanel, TabbedPanelComponent);
|
||||
|
||||
export const SEPARATOR_COMPONENT = 'separator-component';
|
||||
registerComponentType(SEPARATOR_COMPONENT, ModelComponentTypes.Separator, SeparatorComponent);
|
||||
|
||||
@@ -9,10 +9,15 @@ export interface IDashboardTab {
|
||||
provider: string | string[];
|
||||
publisher: string;
|
||||
description?: string;
|
||||
container?: {
|
||||
[key: string]: any;
|
||||
};
|
||||
container?: { [key: string]: any };
|
||||
when?: string;
|
||||
alwaysShow?: boolean;
|
||||
isHomeTab?: boolean;
|
||||
group?: string;
|
||||
iconClass?: string;
|
||||
}
|
||||
|
||||
export interface IDashboardTabGroup {
|
||||
id: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IBootstrapParams, ISelector } from 'sql/workbench/services/bootstrap/common/bootstrapParams';
|
||||
import { startsWith } from 'vs/base/common/strings';
|
||||
import { PanelModule } from 'sql/base/browser/ui/panel/panel.module';
|
||||
|
||||
export const DialogModule = (params, selector: string, instantiationService: IInstantiationService): any => {
|
||||
|
||||
@@ -50,7 +51,8 @@ export const DialogModule = (params, selector: string, instantiationService: IIn
|
||||
imports: [
|
||||
FormsModule,
|
||||
CommonModule,
|
||||
BrowserModule
|
||||
BrowserModule,
|
||||
PanelModule
|
||||
],
|
||||
providers: [
|
||||
{ provide: APP_BASE_HREF, useValue: '/' },
|
||||
|
||||
Reference in New Issue
Block a user