mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Merge dashboardCommandCenter to master (#697)
* Initial work of adding tab in the dashboard (#526) * refactor dashboard to have the home tab * formatting * fix grid layout issue * fix initailize issue in database dashboard * Add action bar to the panel and add close tab to the dashboard (#562) * add action bar to the panel and add close tab to the dashboard * formatting * Tab contribution (#564) * added contrib * disabled edit for extensions; fixed new name for insights contrib * fix merge issue * move file * formatting * fix builds * moving imports * Expand on tab contrib (#581) * added contrib * disabled edit for extensions; fixed new name for insights contrib * fix merge issue * move file * formatting * fix builds * adding to contrib * updated contrib * format * moving imports * updated contribution to map to current design * implemented actually using provider and edition filtering * Refactor and fix issues in close tab and add the placeholder for pin tab (#588) * refactor and fix issues in close tab and add the placeholder for pin tab * formatting * remove the redundant code * add clear all tabs in dashboard page init * Initial work for adding a feature tab dialog (#594) * initial work for add new dashboard tab * formatting * fix add panel action issue * fix breaking change * fix issues and tab and panels * formatting * minor fix * address comments * Add tab status to add extension tab dialog (#610) * add tab status to add extension tab dialog * add tab status to add extension tab dialog * rename add feature tab action * address comments * Webview widget (#618) * getting closer * webview widget now works * fix problem with rerendering webview * formatting * ensure that webview only shows up for extensions * formatting * comments * fix more compile issues * Change dashboard page init (#640) * changed init of serverpage * formatting * Webview tab (#638) * getting closer * webview widget now works * fix problem with rerendering webview * formatting * ensure that webview only shows up for extensions * formatting * comments * fix more compile issues * refacting stuff * added inital webview tab * piped through messaging and tested * Implement pin/unpin feature and always on tabs (#629) * implement pin/unpin feature * fix issue where insight can't be loaded after reopen * fix tab look and feel * implement always show tabs * make AddFeatureTabAction to track always show and pinned tabs * formatting * make dashboard tabs looks like the UX design * load always show before pinned tab * fix regression in panel for restore and connection dialog * fix merge conflict * don't worry about no widgets if its a webview (#656) * expose the dashboard server info when a webview is rendering (#644) * Fix few issues in dashboard command center (#655) * fix reloading insight wigets and create new tab when there is no extension * show possible tabIDs in the setting file * formatting * address comment * fix import name * fixes problem with size of webview widget being wrong (#654) * Refactor tab contribution to support content type (#685) * refactor tab contribution to support content type * formatting * address comment * fix rendering tab issue (#694) * Add layout option to panel for supporting horizontal and vertical navigation bar (#700) * Add left navigation panel for inner tab in the dashboard * add layout option in panel * remove panel option in dashboard Page
This commit is contained in:
@@ -6,7 +6,7 @@
|
|||||||
"vscode": "*"
|
"vscode": "*"
|
||||||
},
|
},
|
||||||
"contributes": {
|
"contributes": {
|
||||||
"insights": [
|
"dashboard.insights": [
|
||||||
{
|
{
|
||||||
"id": "query-data-store-db-insight",
|
"id": "query-data-store-db-insight",
|
||||||
"contrib": {
|
"contrib": {
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ export class WebViewDialog extends Modal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public set headerTitle(value: string) {
|
public set headerTitle(value: string) {
|
||||||
this._headerTitle = value
|
this._headerTitle = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get headerTitle(): string {
|
public get headerTitle(): string {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
.tabbedPanel {
|
.tabbedPanel {
|
||||||
border-top-color: rgba(128, 128, 128, 0.35);
|
border-top-color: rgba(128, 128, 128, 0.35);
|
||||||
border-top-width: 1;
|
border-top-width: 1px;
|
||||||
border-top-style: solid;
|
border-top-style: solid;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
@@ -19,15 +19,14 @@
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
flex-flow: row;
|
|
||||||
line-height: 35px;
|
line-height: 35px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabbedPanel .tabList > .tab {
|
.tabbedPanel .tabList .tab {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabbedPanel .tabList > .tab > .tabLabel {
|
.tabbedPanel .tabList .tab .tabLabel {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
margin-left: 16px;
|
margin-left: 16px;
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
@@ -56,3 +55,29 @@
|
|||||||
.composite.title ~ tab.fullsize > :first-child {
|
.composite.title ~ tab.fullsize > :first-child {
|
||||||
height: calc(100% - 38px);
|
height: calc(100% - 38px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tabbedPanel .title-actions .panel-actions .actions-container {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabbedPanel.vertical {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabbedPanel.vertical > .title {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabbedPanel.vertical > .tab-content {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabbedPanel.vertical > .title > .tabList {
|
||||||
|
flex-flow: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabbedPanel.horizontal > .title > .tabList {
|
||||||
|
flex-flow: row;
|
||||||
|
}
|
||||||
@@ -3,11 +3,14 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { Component, ContentChildren, QueryList, AfterContentInit, Inject, forwardRef, NgZone, OnInit, Input } from '@angular/core';
|
import { Component, ContentChildren, QueryList, AfterContentInit, Inject, forwardRef, NgZone, OnInit, Input, EventEmitter, Output, ViewChild, ElementRef, OnChanges, OnDestroy, ViewChildren, AfterViewInit } from '@angular/core';
|
||||||
|
|
||||||
import { TabComponent } from './tab.component';
|
import { TabComponent } from './tab.component';
|
||||||
|
import { TabHeaderComponent } from './tabHeader.component';
|
||||||
import './panelStyles';
|
import './panelStyles';
|
||||||
|
|
||||||
|
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||||
|
import { Action } from 'vs/base/common/actions';
|
||||||
import * as types from 'vs/base/common/types';
|
import * as types from 'vs/base/common/types';
|
||||||
import { mixin } from 'vs/base/common/objects';
|
import { mixin } from 'vs/base/common/objects';
|
||||||
|
|
||||||
@@ -16,40 +19,65 @@ export interface IPanelOptions {
|
|||||||
* Whether or not to show the tabs if there is only one tab present
|
* Whether or not to show the tabs if there is only one tab present
|
||||||
*/
|
*/
|
||||||
showTabsWhenOne?: boolean;
|
showTabsWhenOne?: boolean;
|
||||||
|
layout?: NavigationBarLayout;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum NavigationBarLayout {
|
||||||
|
horizontal = 0,
|
||||||
|
vertical = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultOptions: IPanelOptions = {
|
const defaultOptions: IPanelOptions = {
|
||||||
showTabsWhenOne: true
|
showTabsWhenOne: true,
|
||||||
|
layout: NavigationBarLayout.horizontal
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const verticalLayout = 'vertical';
|
||||||
|
const horizontalLayout = 'horizontal';
|
||||||
|
|
||||||
|
let idPool = 0;
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'panel',
|
selector: 'panel',
|
||||||
template: `
|
template: `
|
||||||
<div class="tabbedPanel fullsize">
|
<div class="tabbedPanel fullsize" #tabbedPanel style="position: absolute">
|
||||||
<div *ngIf="!options.showTabsWhenOne ? _tabs.length !== 1 : true" class="composite title">
|
<div *ngIf="!options.showTabsWhenOne ? _tabs.length !== 1 : true" class="composite title">
|
||||||
<div class="tabList">
|
<div class="tabList">
|
||||||
<div *ngFor="let tab of _tabs" class="tab" (click)="selectTab(tab)">
|
<div *ngFor="let tab of _tabs">
|
||||||
<a class="tabLabel" [class.active]="tab.active">
|
<tab-header [tab]="tab" (onSelectTab)='selectTab($event)' (onCloseTab)='closeTab($event)'> </tab-header>
|
||||||
{{tab.title}}
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="title-actions">
|
<div class="title-actions">
|
||||||
|
<div #panelActionbar class="panel-actions" style="flex: 0 0 auto; align-self: end; margin-top: auto; margin-bottom: auto;" >
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ng-content class="fullsize"></ng-content>
|
<div class="tab-content fullsize">
|
||||||
|
<ng-content class="fullsize"></ng-content>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
export class PanelComponent implements AfterContentInit, OnInit {
|
export class PanelComponent implements AfterContentInit, OnInit, OnChanges, OnDestroy, AfterViewInit {
|
||||||
@Input() public options: IPanelOptions;
|
@Input() public options: IPanelOptions;
|
||||||
|
@Input() public actions: Array<Action>;
|
||||||
@ContentChildren(TabComponent) private _tabs: QueryList<TabComponent>;
|
@ContentChildren(TabComponent) private _tabs: QueryList<TabComponent>;
|
||||||
private _activeTab: TabComponent;
|
@ViewChildren(TabHeaderComponent) private _headerTabs: QueryList<TabHeaderComponent>;
|
||||||
|
|
||||||
|
@Output() public onTabChange = new EventEmitter<TabComponent>();
|
||||||
|
@Output() public onTabClose = new EventEmitter<TabComponent>();
|
||||||
|
|
||||||
|
private _activeTab: TabComponent;
|
||||||
|
private _actionbar: ActionBar;
|
||||||
|
private _mru: TabComponent[];
|
||||||
|
|
||||||
|
@ViewChild('panelActionbar', { read: ElementRef }) private _actionbarRef: ElementRef;
|
||||||
|
@ViewChild('tabbedPanel', { read: ElementRef }) private _tabbedPanelRef: ElementRef;
|
||||||
constructor( @Inject(forwardRef(() => NgZone)) private _zone: NgZone) { }
|
constructor( @Inject(forwardRef(() => NgZone)) private _zone: NgZone) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.options = mixin(this.options || {}, defaultOptions, false);
|
this.options = mixin(this.options || {}, defaultOptions, false);
|
||||||
|
this._mru = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterContentInit(): void {
|
ngAfterContentInit(): void {
|
||||||
@@ -59,11 +87,38 @@ export class PanelComponent implements AfterContentInit, OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
if (this.options.layout === NavigationBarLayout.horizontal) {
|
||||||
|
(<HTMLElement>this._tabbedPanelRef.nativeElement).classList.add(horizontalLayout);
|
||||||
|
} else {
|
||||||
|
(<HTMLElement>this._tabbedPanelRef.nativeElement).classList.add(verticalLayout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(): void {
|
||||||
|
if (this._actionbarRef && !this._actionbar) {
|
||||||
|
this._actionbar = new ActionBar(this._actionbarRef.nativeElement);
|
||||||
|
}
|
||||||
|
if (this.actions && this._actionbar) {
|
||||||
|
this._actionbar.clear();
|
||||||
|
this._actionbar.push(this.actions, { icon: true, label: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
if (this._actionbar) {
|
||||||
|
this._actionbar.dispose();
|
||||||
|
}
|
||||||
|
if (this.actions && this.actions.length > 0) {
|
||||||
|
this.actions.forEach((action) => action.dispose());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select a tab based on index (unrecommended)
|
* Select a tab based on index (unrecommended)
|
||||||
* @param index index of tab in the html
|
* @param index index of tab in the html
|
||||||
*/
|
*/
|
||||||
selectTab(index: number)
|
selectTab(index: number);
|
||||||
/**
|
/**
|
||||||
* Select a tab based on the identifier that was passed into the tab
|
* Select a tab based on the identifier that was passed into the tab
|
||||||
* @param identifier specified identifer of the tab
|
* @param identifier specified identifer of the tab
|
||||||
@@ -85,14 +140,67 @@ export class PanelComponent implements AfterContentInit, OnInit {
|
|||||||
tab = this._tabs.find(i => i.identifier === input);
|
tab = this._tabs.find(i => i.identifier === input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// since we need to compare identifiers in this next step we are going to go through and make sure all tabs have one
|
||||||
|
this._tabs.forEach(i => {
|
||||||
|
if (!i.identifier) {
|
||||||
|
i.identifier = 'tabIndex_' + idPool++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this._activeTab && tab === this._activeTab) {
|
||||||
|
this.onTabChange.emit(tab);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this._zone.run(() => {
|
this._zone.run(() => {
|
||||||
if (this._activeTab) {
|
if (this._activeTab) {
|
||||||
this._activeTab.active = false;
|
this._activeTab.active = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._activeTab = tab;
|
this._activeTab = tab;
|
||||||
|
this.setMostRecentlyUsed(tab);
|
||||||
this._activeTab.active = true;
|
this._activeTab.active = true;
|
||||||
|
|
||||||
|
// Make the tab header focus on the new selected tab
|
||||||
|
let activeTabHeader = this._headerTabs.find(i => i.tab === this._activeTab);
|
||||||
|
if (activeTabHeader) {
|
||||||
|
activeTabHeader.focusOnTabHeader();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onTabChange.emit(tab);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private findAndRemoveTabFromMRU(tab: TabComponent): void {
|
||||||
|
let mruIndex = this._mru.findIndex(i => i === tab);
|
||||||
|
|
||||||
|
if (mruIndex !== -1) {
|
||||||
|
// Remove old index
|
||||||
|
this._mru.splice(mruIndex, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private setMostRecentlyUsed(tab: TabComponent): void {
|
||||||
|
this.findAndRemoveTabFromMRU(tab);
|
||||||
|
|
||||||
|
// Set tab to front
|
||||||
|
this._mru.unshift(tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close a tab
|
||||||
|
* @param tab tab to close
|
||||||
|
*/
|
||||||
|
closeTab(tab: TabComponent) {
|
||||||
|
this.onTabClose.emit(tab);
|
||||||
|
|
||||||
|
// remove the closed tab from mru
|
||||||
|
this.findAndRemoveTabFromMRU(tab);
|
||||||
|
|
||||||
|
// Open the most recent tab
|
||||||
|
if (this._mru.length > 0) {
|
||||||
|
this.selectTab(this._mru[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,12 @@ import { NgModule } from '@angular/core';
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
import { TabComponent } from './tab.component';
|
import { TabComponent } from './tab.component';
|
||||||
|
import { TabHeaderComponent } from './tabHeader.component';
|
||||||
import { PanelComponent } from './panel.component';
|
import { PanelComponent } from './panel.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [CommonModule],
|
imports: [CommonModule],
|
||||||
exports: [TabComponent, PanelComponent],
|
exports: [TabComponent, PanelComponent],
|
||||||
declarations: [TabComponent, PanelComponent]
|
declarations: [TabComponent, TabHeaderComponent, PanelComponent]
|
||||||
})
|
})
|
||||||
export class PanelModule { }
|
export class PanelModule { }
|
||||||
@@ -87,21 +87,23 @@ export class TabbedPanel extends Disposable implements IThemable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _createTab(tab: IInternalPanelTab): void {
|
private _createTab(tab: IInternalPanelTab): void {
|
||||||
|
let tabHeaderElement = $('.tab-header');
|
||||||
|
tabHeaderElement.attr('tabindex', '0');
|
||||||
let tabElement = $('.tab');
|
let tabElement = $('.tab');
|
||||||
tabElement.attr('tabindex', '0');
|
tabHeaderElement.append(tabElement);
|
||||||
let tabLabel = $('a.tabLabel');
|
let tabLabel = $('a.tabLabel');
|
||||||
tabLabel.safeInnerHtml(tab.title);
|
tabLabel.safeInnerHtml(tab.title);
|
||||||
tabElement.append(tabLabel);
|
tabElement.append(tabLabel);
|
||||||
tabElement.on(EventType.CLICK, e => this.showTab(tab.identifier));
|
tabHeaderElement.on(EventType.CLICK, e => this.showTab(tab.identifier));
|
||||||
tabElement.on(EventType.KEY_DOWN, (e: KeyboardEvent) => {
|
tabHeaderElement.on(EventType.KEY_DOWN, (e: KeyboardEvent) => {
|
||||||
let event = new StandardKeyboardEvent(e);
|
let event = new StandardKeyboardEvent(e);
|
||||||
if (event.equals(KeyCode.Enter)) {
|
if (event.equals(KeyCode.Enter)) {
|
||||||
this.showTab(tab.identifier);
|
this.showTab(tab.identifier);
|
||||||
e.stopImmediatePropagation();
|
e.stopImmediatePropagation();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.$tabList.append(tabElement);
|
this.$tabList.append(tabHeaderElement);
|
||||||
tab.header = tabElement;
|
tab.header = tabHeaderElement;
|
||||||
tab.label = tabLabel;
|
tab.label = tabLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,12 +114,14 @@ export class TabbedPanel extends Disposable implements IThemable {
|
|||||||
|
|
||||||
if (this._shownTab) {
|
if (this._shownTab) {
|
||||||
this._tabMap.get(this._shownTab).label.removeClass('active');
|
this._tabMap.get(this._shownTab).label.removeClass('active');
|
||||||
|
this._tabMap.get(this._shownTab).header.removeClass('active');
|
||||||
}
|
}
|
||||||
|
|
||||||
this._shownTab = id;
|
this._shownTab = id;
|
||||||
this.$body.clearChildren();
|
this.$body.clearChildren();
|
||||||
let tab = this._tabMap.get(this._shownTab);
|
let tab = this._tabMap.get(this._shownTab);
|
||||||
tab.label.addClass('active');
|
tab.label.addClass('active');
|
||||||
|
tab.header.addClass('active');
|
||||||
tab.view.render(this.$body.getHTMLElement());
|
tab.view.render(this.$body.getHTMLElement());
|
||||||
this._onTabChange.fire(id);
|
this._onTabChange.fire(id);
|
||||||
if (this._currentDimensions) {
|
if (this._currentDimensions) {
|
||||||
|
|||||||
@@ -15,11 +15,15 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
|||||||
const titleActiveBorder = theme.getColor(PANEL_ACTIVE_TITLE_BORDER);
|
const titleActiveBorder = theme.getColor(PANEL_ACTIVE_TITLE_BORDER);
|
||||||
if (titleActive || titleActiveBorder) {
|
if (titleActive || titleActiveBorder) {
|
||||||
collector.addRule(`
|
collector.addRule(`
|
||||||
.tabbedPanel > .title > .tabList > .tab:hover .tabLabel,
|
.tabbedPanel > .title > .tabList .tab:hover .tabLabel,
|
||||||
.tabbedPanel > .title > .tabList > .tab .tabLabel.active {
|
.tabbedPanel > .title > .tabList .tab .tabLabel.active {
|
||||||
color: ${titleActive};
|
color: ${titleActive};
|
||||||
border-bottom-color: ${titleActiveBorder};
|
border-bottom-color: ${titleActiveBorder};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tabbedPanel > .title > .tabList .tab-header.active {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,7 +31,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
|||||||
const titleInactive = theme.getColor(PANEL_INACTIVE_TITLE_FOREGROUND);
|
const titleInactive = theme.getColor(PANEL_INACTIVE_TITLE_FOREGROUND);
|
||||||
if (titleInactive) {
|
if (titleInactive) {
|
||||||
collector.addRule(`
|
collector.addRule(`
|
||||||
.tabbedPanel > .title > .tabList > .tab .tabLabel {
|
.tabbedPanel > .title > .tabList .tab .tabLabel {
|
||||||
color: ${titleInactive};
|
color: ${titleInactive};
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
@@ -37,7 +41,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
|||||||
const focusBorderColor = theme.getColor(focusBorder);
|
const focusBorderColor = theme.getColor(focusBorder);
|
||||||
if (focusBorderColor) {
|
if (focusBorderColor) {
|
||||||
collector.addRule(`
|
collector.addRule(`
|
||||||
.tabbedPanel > .title > .tabList > .tab .tabLabel:focus {
|
.tabbedPanel > .title > .tabList .tab .tabLabel:focus {
|
||||||
color: ${titleActive};
|
color: ${titleActive};
|
||||||
border-bottom-color: ${focusBorderColor} !important;
|
border-bottom-color: ${focusBorderColor} !important;
|
||||||
border-bottom: 1px solid;
|
border-bottom: 1px solid;
|
||||||
@@ -49,20 +53,17 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
|||||||
// Styling with Outline color (e.g. high contrast theme)
|
// Styling with Outline color (e.g. high contrast theme)
|
||||||
const outline = theme.getColor(activeContrastBorder);
|
const outline = theme.getColor(activeContrastBorder);
|
||||||
if (outline) {
|
if (outline) {
|
||||||
const outline = theme.getColor(activeContrastBorder);
|
|
||||||
|
|
||||||
collector.addRule(`
|
collector.addRule(`
|
||||||
.tabbedPanel > .title > .tabList > .tab .tabLabel.active,
|
.tabbedPanel > .title > .tabList .tab-header.active,
|
||||||
.tabbedPanel > .title > .tabList > .tab .tabLabel:hover {
|
.tabbedPanel > .title > .tabList .tab-header:hover {
|
||||||
outline-color: ${outline};
|
outline-color: ${outline};
|
||||||
outline-width: 1px;
|
outline-width: 1px;
|
||||||
outline-style: solid;
|
outline-style: solid;
|
||||||
border-bottom: none;
|
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
outline-offset: 3px;
|
outline-offset: -5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabbedPanel > .title > .tabList > .tab .tabLabel:hover:not(.active) {
|
.tabbedPanel > .title > .tabList .tab-header:hover:not(.active) {
|
||||||
outline-style: dashed;
|
outline-style: dashed;
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
import { Component, Input, ContentChild } from '@angular/core';
|
import { Component, Input, ContentChild, OnDestroy } from '@angular/core';
|
||||||
|
|
||||||
|
import { Action } from 'vs/base/common/actions';
|
||||||
|
|
||||||
export abstract class TabChild {
|
export abstract class TabChild {
|
||||||
public abstract layout(): void;
|
public abstract layout(): void;
|
||||||
@@ -16,9 +18,11 @@ export abstract class TabChild {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
export class TabComponent {
|
export class TabComponent implements OnDestroy {
|
||||||
@ContentChild(TabChild) private _child: TabChild;
|
@ContentChild(TabChild) private _child: TabChild;
|
||||||
@Input() public title: string;
|
@Input() public title: string;
|
||||||
|
@Input() public canClose: boolean;
|
||||||
|
@Input() public actions: Array<Action>;
|
||||||
public _active = false;
|
public _active = false;
|
||||||
@Input() public identifier: string;
|
@Input() public identifier: string;
|
||||||
|
|
||||||
@@ -32,4 +36,11 @@ export class TabComponent {
|
|||||||
public get active(): boolean {
|
public get active(): boolean {
|
||||||
return this._active;
|
return this._active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
if (this.actions && this.actions.length > 0) {
|
||||||
|
this.actions.forEach((action) => action.dispose());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
29
src/sql/base/browser/ui/panel/tabActions.ts
Normal file
29
src/sql/base/browser/ui/panel/tabActions.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
import { Action } from 'vs/base/common/actions';
|
||||||
|
import * as nls from 'vs/nls';
|
||||||
|
import { TPromise } from 'vs/base/common/winjs.base';
|
||||||
|
|
||||||
|
export class CloseTabAction extends Action {
|
||||||
|
private static readonly ID = 'closeTab';
|
||||||
|
private static readonly LABEL = nls.localize('closeTab', "Close");
|
||||||
|
private static readonly ICON = 'close';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private closeFn: () => void,
|
||||||
|
private context: any // this
|
||||||
|
) {
|
||||||
|
super(CloseTabAction.ID, CloseTabAction.LABEL, CloseTabAction.ICON);
|
||||||
|
}
|
||||||
|
|
||||||
|
run(): TPromise<boolean> {
|
||||||
|
try {
|
||||||
|
this.closeFn.apply(this.context);
|
||||||
|
return TPromise.as(true);
|
||||||
|
} catch (e) {
|
||||||
|
return TPromise.as(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
85
src/sql/base/browser/ui/panel/tabHeader.component.ts
Normal file
85
src/sql/base/browser/ui/panel/tabHeader.component.ts
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
import 'vs/css!sql/media/icons/common-icons';
|
||||||
|
import 'vs/css!./tabHeader';
|
||||||
|
|
||||||
|
import { Component, AfterContentInit, OnDestroy, Input, Output, ElementRef, ViewChild, EventEmitter } from '@angular/core';
|
||||||
|
|
||||||
|
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||||
|
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||||
|
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||||
|
import * as DOM from 'vs/base/browser/dom';
|
||||||
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
|
|
||||||
|
import { TabComponent } from './tab.component';
|
||||||
|
import { CloseTabAction } from './tabActions';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'tab-header',
|
||||||
|
template: `
|
||||||
|
<div #actionHeader class="tab-header" style="display: flex; flex: 0 0; flex-direction: row;" [class.active]="tab.active" tabindex="0" (keyup)="onKey($event)">
|
||||||
|
<span class="tab" (click)="selectTab(tab)">
|
||||||
|
<a class="tabLabel" [class.active]="tab.active">
|
||||||
|
{{tab.title}}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<span #actionbar style="flex: 0 0 auto; align-self: end; margin-top: auto; margin-bottom: auto;" ></span>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
export class TabHeaderComponent extends Disposable implements AfterContentInit, OnDestroy {
|
||||||
|
@Input() public tab: TabComponent;
|
||||||
|
@Output() public onSelectTab: EventEmitter<TabComponent> = new EventEmitter<TabComponent>();
|
||||||
|
@Output() public onCloseTab: EventEmitter<TabComponent> = new EventEmitter<TabComponent>();
|
||||||
|
|
||||||
|
private _actionbar: ActionBar;
|
||||||
|
|
||||||
|
@ViewChild('actionHeader', { read: ElementRef }) private _actionHeaderRef: ElementRef;
|
||||||
|
@ViewChild('actionbar', { read: ElementRef }) private _actionbarRef: ElementRef;
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterContentInit(): void {
|
||||||
|
this._actionbar = new ActionBar(this._actionbarRef.nativeElement);
|
||||||
|
if (this.tab.actions) {
|
||||||
|
this._actionbar.push(this.tab.actions, { icon: true, label: false });
|
||||||
|
}
|
||||||
|
if (this.tab.canClose) {
|
||||||
|
let closeAction = this._register(new CloseTabAction(this.closeTab, this));
|
||||||
|
this._actionbar.push(closeAction, { icon: true, label: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
if (this._actionbar) {
|
||||||
|
this._actionbar.dispose();
|
||||||
|
}
|
||||||
|
this.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
selectTab(tab: TabComponent) {
|
||||||
|
this.onSelectTab.emit(tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
closeTab() {
|
||||||
|
this.onCloseTab.emit(this.tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
focusOnTabHeader() {
|
||||||
|
let header = <HTMLElement>this._actionHeaderRef.nativeElement;
|
||||||
|
header.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
onKey(e: Event) {
|
||||||
|
if (DOM.isAncestor(<HTMLElement>e.target, this._actionHeaderRef.nativeElement) && e instanceof KeyboardEvent) {
|
||||||
|
let event = new StandardKeyboardEvent(e);
|
||||||
|
if (event.equals(KeyCode.Enter)) {
|
||||||
|
this.onSelectTab.emit(this.tab);
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/sql/base/browser/ui/panel/tabHeader.css
Normal file
20
src/sql/base/browser/ui/panel/tabHeader.css
Normal file
@@ -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.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
tab-header .action-item .action-label {
|
||||||
|
opacity: 0;
|
||||||
|
padding: 8px;
|
||||||
|
margin-top: 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
tab-header .action-item {
|
||||||
|
line-height: 1.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
tab-header .tab-header.active .action-label, /* always show it for active tab */
|
||||||
|
tab-header .tab-header:hover .action-label, /* always show it on hover */
|
||||||
|
tab-header .tab-header:focus .action-label { /* always show it on focus */
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
@@ -40,3 +40,4 @@ export const ServerGroups = 'ServerGroups';
|
|||||||
export const Accounts = 'Accounts';
|
export const Accounts = 'Accounts';
|
||||||
export const FireWallRule = 'FirewallRule';
|
export const FireWallRule = 'FirewallRule';
|
||||||
export const AutoOAuth = 'AutoOAuth';
|
export const AutoOAuth = 'AutoOAuth';
|
||||||
|
export const AddNewDashboardTab = 'AddNewDashboardTab';
|
||||||
|
|||||||
42
src/sql/data.d.ts
vendored
42
src/sql/data.d.ts
vendored
@@ -1441,6 +1441,48 @@ declare module 'data' {
|
|||||||
postMessage(message: any): Thenable<any>;
|
postMessage(message: any): Thenable<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DashboardWebview {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raised when the webview posts a message.
|
||||||
|
*/
|
||||||
|
readonly onMessage: vscode.Event<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raised when the webview closed.
|
||||||
|
*/
|
||||||
|
readonly onClosed: vscode.Event<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post a message to the webview.
|
||||||
|
*
|
||||||
|
* @param message Body of the message.
|
||||||
|
*/
|
||||||
|
postMessage(message: any): Thenable<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The connection info for the dashboard the webview exists on
|
||||||
|
*/
|
||||||
|
readonly connection: connection.Connection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The info on the server for the webview dashboard
|
||||||
|
*/
|
||||||
|
readonly serverInfo: ServerInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contents of the dialog body.
|
||||||
|
*/
|
||||||
|
html: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace dashboard {
|
||||||
|
/**
|
||||||
|
* Register a provider for a webview widget
|
||||||
|
*/
|
||||||
|
export function registerWebviewProvider(widgetId: string, handler: (webview: DashboardWebview) => void): void;
|
||||||
|
}
|
||||||
|
|
||||||
export namespace window {
|
export namespace window {
|
||||||
/**
|
/**
|
||||||
* creates a dialog
|
* creates a dialog
|
||||||
|
|||||||
@@ -155,6 +155,15 @@
|
|||||||
background: url('ellipsis.svg') center center no-repeat;
|
background: url('ellipsis.svg') center center no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hc-black .icon.new,
|
||||||
|
.vs-dark .icon.new {
|
||||||
|
background: url('new_inverse.svg') center center no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vs .icon.new {
|
||||||
|
background: url('new.svg') center center no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
.hc-black .icon.new-query,
|
.hc-black .icon.new-query,
|
||||||
.vs-dark .icon.new-query {
|
.vs-dark .icon.new-query {
|
||||||
background: url('newquery_inverse.svg') center center no-repeat;
|
background: url('newquery_inverse.svg') center center no-repeat;
|
||||||
@@ -181,3 +190,21 @@
|
|||||||
.vs .icon.edit {
|
.vs .icon.edit {
|
||||||
background: url('edit.svg') center center no-repeat;
|
background: url('edit.svg') center center no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hc-black .icon.pin,
|
||||||
|
.vs-dark .icon.pin {
|
||||||
|
background: url('pin_inverse.svg') center center no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vs .icon.pin {
|
||||||
|
background: url('pin.svg') center center no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hc-black .icon.unpin,
|
||||||
|
.vs-dark .icon.unpin {
|
||||||
|
background: url('unpin_inverse.svg') center center no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vs .icon.unpin {
|
||||||
|
background: url('unpin.svg') center center no-repeat;
|
||||||
|
}
|
||||||
1
src/sql/media/icons/new.svg
Normal file
1
src/sql/media/icons/new.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#231f20;}.cls-2{fill:#212121;}</style></defs><title>new_16x16</title><polygon class="cls-1" points="13.59 2.21 13.58 2.22 13.58 2.2 13.59 2.21"/><path class="cls-2" d="M16,7.5v1H8.5V16h-1V8.5H0v-1H7.5V0h1V7.5Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 336 B |
1
src/sql/media/icons/new_inverse.svg
Normal file
1
src/sql/media/icons/new_inverse.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>new_inverse_16x16</title><polygon class="cls-1" points="13.59 2.21 13.58 2.22 13.58 2.2 13.59 2.21"/><path class="cls-1" d="M16,7.5v1H8.5V16h-1V8.5H0v-1H7.5V0h1V7.5Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 320 B |
1
src/sql/media/icons/pin.svg
Normal file
1
src/sql/media/icons/pin.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>pin</title><path d="M15.55,5.84q-.26.26-.5.47a3,3,0,0,1-.51.36,2.41,2.41,0,0,1-.57.23,2.73,2.73,0,0,1-.7.08,2.67,2.67,0,0,1-.51,0l-3,3a3.51,3.51,0,0,1,.15.61,4.21,4.21,0,0,1,0,.63,3.89,3.89,0,0,1-.1.92,3.44,3.44,0,0,1-.29.78,4,4,0,0,1-.46.7q-.27.33-.62.68l-3-3L1.06,15.65,0,16l.35-1.06L4.8,10.5l-3-3,.35-.35A3.77,3.77,0,0,1,3.39,6.3,3.94,3.94,0,0,1,6.09,6.2l3-3a2.67,2.67,0,0,1,0-.51A2.7,2.7,0,0,1,9.09,2a2.48,2.48,0,0,1,.23-.57A3,3,0,0,1,9.68,1q.22-.25.47-.5ZM13.27,6a1.7,1.7,0,0,0,.81-.2L10.21,1.92a1.7,1.7,0,0,0-.2.81,1.58,1.58,0,0,0,.05.41c0,.13.07.26.11.39L6.33,7.37,6,7.23l-.34-.12L5.26,7a2.63,2.63,0,0,0-.39,0A2.91,2.91,0,0,0,4,7.14a2.79,2.79,0,0,0-.78.38l5.26,5.26A2.88,2.88,0,0,0,8.87,12,2.8,2.8,0,0,0,9,11.14a2.63,2.63,0,0,0,0-.39,2.77,2.77,0,0,0-.08-.36A3.49,3.49,0,0,0,8.77,10l-.15-.37,3.84-3.84.39.11A1.58,1.58,0,0,0,13.27,6Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 947 B |
1
src/sql/media/icons/pin_inverse.svg
Normal file
1
src/sql/media/icons/pin_inverse.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>pin_inverse</title><path class="cls-1" d="M15.55,5.84q-.26.26-.5.47a3,3,0,0,1-.51.36,2.41,2.41,0,0,1-.57.23,2.73,2.73,0,0,1-.7.08,2.67,2.67,0,0,1-.51,0l-3,3a3.51,3.51,0,0,1,.15.61,4.21,4.21,0,0,1,0,.63,3.89,3.89,0,0,1-.1.92,3.44,3.44,0,0,1-.29.78,4,4,0,0,1-.46.7q-.27.33-.62.68l-3-3L1.06,15.65,0,16l.35-1.06L4.8,10.5l-3-3,.35-.35A3.77,3.77,0,0,1,3.39,6.3,3.94,3.94,0,0,1,6.09,6.2l3-3a2.67,2.67,0,0,1,0-.51A2.7,2.7,0,0,1,9.09,2a2.48,2.48,0,0,1,.23-.57A3,3,0,0,1,9.68,1q.22-.25.47-.5ZM13.27,6a1.7,1.7,0,0,0,.81-.2L10.21,1.92a1.7,1.7,0,0,0-.2.81,1.58,1.58,0,0,0,.05.41c0,.13.07.26.11.39L6.33,7.37,6,7.23l-.34-.12L5.26,7a2.63,2.63,0,0,0-.39,0A2.91,2.91,0,0,0,4,7.14a2.79,2.79,0,0,0-.78.38l5.26,5.26A2.88,2.88,0,0,0,8.87,12,2.8,2.8,0,0,0,9,11.14a2.63,2.63,0,0,0,0-.39,2.77,2.77,0,0,0-.08-.36A3.49,3.49,0,0,0,8.77,10l-.15-.37,3.84-3.84.39.11A1.58,1.58,0,0,0,13.27,6Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1015 B |
1
src/sql/media/icons/unpin.svg
Normal file
1
src/sql/media/icons/unpin.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>unpin</title><path d="M15.55,5.84q-.26.26-.5.47a3,3,0,0,1-.51.36,2.41,2.41,0,0,1-.57.23,2.73,2.73,0,0,1-.7.08,2.67,2.67,0,0,1-.51,0L11.71,8H10.29l2.17-2.17.4.11a1.59,1.59,0,0,0,.41.05,1.7,1.7,0,0,0,.81-.2L10.21,1.92a1.7,1.7,0,0,0-.2.81,1.59,1.59,0,0,0,.05.41c0,.13.07.26.11.4L6.33,7.37,6,7.23l-.34-.12L5.27,7a2.63,2.63,0,0,0-.39,0A3,3,0,0,0,4,7.14a2.77,2.77,0,0,0-.78.38L7,11.29v1.42L5.5,11.2,1.06,15.65,0,16l.35-1.06L4.8,10.5l-3-3,.35-.35A3.74,3.74,0,0,1,3.4,6.3,4,4,0,0,1,4.89,6a3.59,3.59,0,0,1,.61.05,4.51,4.51,0,0,1,.6.14l3-3a2.67,2.67,0,0,1,0-.51A2.7,2.7,0,0,1,9.09,2a2.48,2.48,0,0,1,.23-.57A3,3,0,0,1,9.68,1q.22-.25.47-.5ZM11.5,9a3.41,3.41,0,0,1,1.36.27,3.51,3.51,0,0,1,1.86,1.86,3.54,3.54,0,0,1,0,2.73,3.51,3.51,0,0,1-1.86,1.86,3.54,3.54,0,0,1-2.73,0,3.51,3.51,0,0,1-1.86-1.86,3.54,3.54,0,0,1,0-2.73,3.51,3.51,0,0,1,1.86-1.86A3.41,3.41,0,0,1,11.5,9ZM9,12.5a2.45,2.45,0,0,0,.2,1,2.49,2.49,0,0,0,1.33,1.33,2.45,2.45,0,0,0,1,.2,2.49,2.49,0,0,0,.72-.11,2.43,2.43,0,0,0,.66-.31L9.41,11.12a2.43,2.43,0,0,0-.31.66A2.49,2.49,0,0,0,9,12.5Zm4.59,1.38a2.43,2.43,0,0,0,.31-.66A2.49,2.49,0,0,0,14,12.5a2.38,2.38,0,0,0-.2-1,2.56,2.56,0,0,0-1.33-1.33,2.38,2.38,0,0,0-1-.2,2.49,2.49,0,0,0-.72.11,2.43,2.43,0,0,0-.66.31Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
1
src/sql/media/icons/unpin_inverse.svg
Normal file
1
src/sql/media/icons/unpin_inverse.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>unpin_inverse</title><path class="cls-1" d="M15.55,5.84q-.26.26-.5.47a3,3,0,0,1-.51.36,2.41,2.41,0,0,1-.57.23,2.73,2.73,0,0,1-.7.08,2.67,2.67,0,0,1-.51,0L11.71,8H10.29l2.17-2.17.4.11a1.59,1.59,0,0,0,.41.05,1.7,1.7,0,0,0,.81-.2L10.21,1.92a1.7,1.7,0,0,0-.2.81,1.59,1.59,0,0,0,.05.41c0,.13.07.26.11.4L6.33,7.37,6,7.23l-.34-.12L5.27,7a2.63,2.63,0,0,0-.39,0A3,3,0,0,0,4,7.14a2.77,2.77,0,0,0-.78.38L7,11.29v1.42L5.5,11.2,1.06,15.65,0,16l.35-1.06L4.8,10.5l-3-3,.35-.35A3.74,3.74,0,0,1,3.4,6.3,4,4,0,0,1,4.89,6a3.59,3.59,0,0,1,.61.05,4.51,4.51,0,0,1,.6.14l3-3a2.67,2.67,0,0,1,0-.51A2.7,2.7,0,0,1,9.09,2a2.48,2.48,0,0,1,.23-.57A3,3,0,0,1,9.68,1q.22-.25.47-.5ZM11.5,9a3.41,3.41,0,0,1,1.36.27,3.51,3.51,0,0,1,1.86,1.86,3.54,3.54,0,0,1,0,2.73,3.51,3.51,0,0,1-1.86,1.86,3.54,3.54,0,0,1-2.73,0,3.51,3.51,0,0,1-1.86-1.86,3.54,3.54,0,0,1,0-2.73,3.51,3.51,0,0,1,1.86-1.86A3.41,3.41,0,0,1,11.5,9ZM9,12.5a2.45,2.45,0,0,0,.2,1,2.49,2.49,0,0,0,1.33,1.33,2.45,2.45,0,0,0,1,.2,2.49,2.49,0,0,0,.72-.11,2.43,2.43,0,0,0,.66-.31L9.41,11.12a2.43,2.43,0,0,0-.31.66A2.49,2.49,0,0,0,9,12.5Zm4.59,1.38a2.43,2.43,0,0,0,.31-.66A2.49,2.49,0,0,0,14,12.5a2.38,2.38,0,0,0-.2-1,2.56,2.56,0,0,0-1.33-1.33,2.38,2.38,0,0,0-1-.2,2.49,2.49,0,0,0-.72.11,2.43,2.43,0,0,0-.66.31Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -7,8 +7,12 @@ import * as nls from 'vs/nls';
|
|||||||
import { TPromise } from 'vs/base/common/winjs.base';
|
import { TPromise } from 'vs/base/common/winjs.base';
|
||||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||||
import { IAngularEventingService, AngularEventType } from 'sql/services/angularEventing/angularEventingService';
|
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||||
|
|
||||||
|
import { IAngularEventingService, AngularEventType, IAngularEvent } from 'sql/services/angularEventing/angularEventingService';
|
||||||
|
import { INewDashboardTabDialogService } from 'sql/parts/dashboard/newDashboardTabDialog/interface';
|
||||||
|
import { IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
|
||||||
|
import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils';
|
||||||
export class EditDashboardAction extends Action {
|
export class EditDashboardAction extends Action {
|
||||||
|
|
||||||
private static readonly ID = 'editDashboard';
|
private static readonly ID = 'editDashboard';
|
||||||
@@ -111,3 +115,85 @@ export class DeleteWidgetAction extends Action {
|
|||||||
return TPromise.as(true);
|
return TPromise.as(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class PinUnpinTabAction extends Action {
|
||||||
|
private static readonly ID = 'pinTab';
|
||||||
|
private static readonly PINLABEL = nls.localize('clickToUnpin', "Click to unpin");
|
||||||
|
private static readonly UNPINLABEL = nls.localize('clickToPin', "Click to pin");
|
||||||
|
private static readonly PINICON = 'pin';
|
||||||
|
private static readonly UNPINICON = 'unpin';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private _tabId: string,
|
||||||
|
private _uri: string,
|
||||||
|
private _isPinned: boolean,
|
||||||
|
@IAngularEventingService private angularEventService: IAngularEventingService
|
||||||
|
) {
|
||||||
|
super(PinUnpinTabAction.ID, PinUnpinTabAction.PINLABEL, PinUnpinTabAction.PINICON);
|
||||||
|
this.updatePinStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private updatePinStatus() {
|
||||||
|
if (this._isPinned) {
|
||||||
|
this.label = PinUnpinTabAction.PINLABEL;
|
||||||
|
this.class = PinUnpinTabAction.PINICON;
|
||||||
|
} else {
|
||||||
|
this.label = PinUnpinTabAction.UNPINLABEL;
|
||||||
|
this.class = PinUnpinTabAction.UNPINICON;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public run(): TPromise<boolean> {
|
||||||
|
this._isPinned = !this._isPinned;
|
||||||
|
this.updatePinStatus();
|
||||||
|
this.angularEventService.sendAngularEvent(this._uri, AngularEventType.PINUNPIN_TAB, { tabId: this._tabId, isPinned: this._isPinned });
|
||||||
|
return TPromise.as(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AddFeatureTabAction extends Action {
|
||||||
|
private static readonly ID = 'openInstalledFeatures';
|
||||||
|
private static readonly LABEL = nls.localize('openInstalledFeatures', "Open installed features");
|
||||||
|
private static readonly ICON = 'new';
|
||||||
|
|
||||||
|
private _disposables: IDisposable[] = [];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private _dashboardTabs: Array<IDashboardTab>,
|
||||||
|
private _openedTabs: Array<IDashboardTab>,
|
||||||
|
private _uri: string,
|
||||||
|
@INewDashboardTabDialogService private _newDashboardTabService: INewDashboardTabDialogService,
|
||||||
|
@IAngularEventingService private _angularEventService: IAngularEventingService
|
||||||
|
) {
|
||||||
|
super(AddFeatureTabAction.ID, AddFeatureTabAction.LABEL, AddFeatureTabAction.ICON);
|
||||||
|
this._disposables.push(toDisposableSubscription(this._angularEventService.onAngularEvent(this._uri, (event) => this.handleDashboardEvent(event))));
|
||||||
|
}
|
||||||
|
|
||||||
|
run(): TPromise<boolean> {
|
||||||
|
this._newDashboardTabService.showDialog(this._dashboardTabs, this._openedTabs, this._uri);
|
||||||
|
return TPromise.as(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
super.dispose();
|
||||||
|
this._disposables.forEach((item) => item.dispose());
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleDashboardEvent(event: IAngularEvent): void {
|
||||||
|
switch (event.event) {
|
||||||
|
case AngularEventType.NEW_TABS:
|
||||||
|
let openedTabs = <IDashboardTab[]>event.payload.dashboardTabs;
|
||||||
|
openedTabs.forEach(tab => {
|
||||||
|
let existedTab = this._openedTabs.find(i => i === tab);
|
||||||
|
if (!existedTab) {
|
||||||
|
this._openedTabs.push(tab);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case AngularEventType.CLOSE_TAB:
|
||||||
|
let index = this._openedTabs.findIndex(i => i.id === event.payload.id);
|
||||||
|
this._openedTabs.splice(index, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,14 +5,18 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
-->
|
-->
|
||||||
<div #scrollContainer style="height: 100%">
|
<div #scrollContainer style="height: 100%">
|
||||||
<div #scrollable style="position: relative">
|
<div #scrollable style="position: relative; display: flex; flex-direction: column; height: 100%">
|
||||||
<div #propertiesContainer>
|
<div style="flex: 0 0 auto" #propertiesContainer>
|
||||||
<dashboard-widget-wrapper #properties *ngIf="propertiesWidget" [_config]="propertiesWidget" style="padding-left: 10px; padding-right: 10px; height: 90px; display: block">
|
<dashboard-widget-wrapper #properties *ngIf="propertiesWidget" [_config]="propertiesWidget" style="padding-left: 10px; padding-right: 10px; height: 90px; display: block">
|
||||||
</dashboard-widget-wrapper>
|
</dashboard-widget-wrapper>
|
||||||
</div>
|
</div>
|
||||||
<div [ngGrid]="gridConfig">
|
<panel style="flex: 1 1 auto; position: relative" class="dashboard-panel" (onTabChange)="handleTabChange($event)" (onTabClose)="handleTabClose($event)" [actions]="panelActions">
|
||||||
<dashboard-widget-wrapper *ngFor="let widget of widgets" [(ngGridItem)]="widget.gridItemConfig" [_config]="widget">
|
<tab *ngFor="let tab of tabs" [title]="tab.title" class="fullsize" [identifier]="tab.id" [canClose]="tab.canClose" [actions]="tab.actions">
|
||||||
</dashboard-widget-wrapper>
|
<dashboard-webview-tab *ngIf="getContentType(tab) === 'webview-tab'" [tab]="tab">
|
||||||
</div>
|
</dashboard-webview-tab>
|
||||||
|
<dashboard-widget-tab *ngIf="getContentType(tab) === 'widgets-tab'" [tab]="tab">
|
||||||
|
</dashboard-widget-tab>
|
||||||
|
</tab>
|
||||||
|
</panel>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,17 +4,27 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import 'vs/css!./dashboardPage';
|
import 'vs/css!./dashboardPage';
|
||||||
|
import './dashboardPanelStyles';
|
||||||
|
|
||||||
import { Component, Inject, forwardRef, ViewChild, ElementRef, ViewChildren, QueryList, OnDestroy, ChangeDetectorRef } from '@angular/core';
|
import { Component, Inject, forwardRef, ViewChild, ElementRef, ViewChildren, QueryList, OnDestroy, ChangeDetectorRef } from '@angular/core';
|
||||||
import { NgGridConfig, NgGrid, NgGridItem } from 'angular2-grid';
|
|
||||||
|
|
||||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||||
import { WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
import { WidgetConfig, TabConfig, PinConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||||
import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo';
|
import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo';
|
||||||
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
|
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
|
||||||
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/common/dashboardWidgetWrapper.component';
|
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/common/dashboardWidgetWrapper.component';
|
||||||
import { subscriptionToDisposable } from 'sql/base/common/lifecycle';
|
|
||||||
import { IPropertiesConfig } from 'sql/parts/dashboard/pages/serverDashboardPage.contribution';
|
import { IPropertiesConfig } from 'sql/parts/dashboard/pages/serverDashboardPage.contribution';
|
||||||
|
import { PanelComponent } from 'sql/base/browser/ui/panel/panel.component';
|
||||||
|
import { subscriptionToDisposable } from 'sql/base/common/lifecycle';
|
||||||
|
import { IDashboardRegistry, Extensions as DashboardExtensions, IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
|
||||||
|
import { PinUnpinTabAction, AddFeatureTabAction } from './actions';
|
||||||
|
import { TabComponent } from 'sql/base/browser/ui/panel/tab.component';
|
||||||
|
import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService';
|
||||||
|
import { AngularEventType, IAngularEvent } from 'sql/services/angularEventing/angularEventingService';
|
||||||
|
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
|
||||||
|
import { error } from 'sql/base/common/log';
|
||||||
|
import { WIDGETS_TABS } from 'sql/parts/dashboard/tabs/dashboardWidgetTab.contribution';
|
||||||
|
import { WEBVIEW_TABS } from 'sql/parts/dashboard/tabs/dashboardWebviewTab.contribution';
|
||||||
|
|
||||||
import { Registry } from 'vs/platform/registry/common/platform';
|
import { Registry } from 'vs/platform/registry/common/platform';
|
||||||
import * as types from 'vs/base/common/types';
|
import * as types from 'vs/base/common/types';
|
||||||
@@ -28,9 +38,13 @@ import { IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeS
|
|||||||
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
||||||
import * as themeColors from 'vs/workbench/common/theme';
|
import * as themeColors from 'vs/workbench/common/theme';
|
||||||
import { generateUuid } from 'vs/base/common/uuid';
|
import { generateUuid } from 'vs/base/common/uuid';
|
||||||
import * as objects from 'sql/base/common/objects';
|
import * as objects from 'vs/base/common/objects';
|
||||||
|
import Event, { Emitter } from 'vs/base/common/event';
|
||||||
|
import { Action } from 'vs/base/common/actions';
|
||||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||||
|
|
||||||
|
const dashboardRegistry = Registry.as<IDashboardRegistry>(DashboardExtensions.DashboardContributions);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns whether the provided parameter is a JavaScript Array and each element in the array is a number.
|
* @returns whether the provided parameter is a JavaScript Array and each element in the array is a number.
|
||||||
*/
|
*/
|
||||||
@@ -38,51 +52,6 @@ function isNumberArray(value: any): value is number[] {
|
|||||||
return types.isArray(value) && (<any[]>value).every(elem => types.isNumber(elem));
|
return types.isArray(value) && (<any[]>value).every(elem => types.isNumber(elem));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sorting function for dashboard widgets
|
|
||||||
* In order of priority;
|
|
||||||
* If neither have defined grid positions, they are equivalent
|
|
||||||
* If a has a defined grid position and b does not; a should come first
|
|
||||||
* If both have defined grid positions and have the same row; the one with the smaller col position should come first
|
|
||||||
* If both have defined grid positions but different rows (it doesn't really matter in this case) the lowers row should come first
|
|
||||||
*/
|
|
||||||
function configSorter(a, b): number {
|
|
||||||
if ((!a.gridItemConfig || !a.gridItemConfig.col)
|
|
||||||
&& (!b.gridItemConfig || !b.gridItemConfig.col)) {
|
|
||||||
return 0;
|
|
||||||
} else if (!a.gridItemConfig || !a.gridItemConfig.col) {
|
|
||||||
return 1;
|
|
||||||
} else if (!b.gridItemConfig || !b.gridItemConfig.col) {
|
|
||||||
return -1;
|
|
||||||
} else if (a.gridItemConfig.row === b.gridItemConfig.row) {
|
|
||||||
if (a.gridItemConfig.col < b.gridItemConfig.col) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a.gridItemConfig.col === b.gridItemConfig.col) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a.gridItemConfig.col > b.gridItemConfig.col) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (a.gridItemConfig.row < b.gridItemConfig.row) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a.gridItemConfig.row === b.gridItemConfig.row) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a.gridItemConfig.row > b.gridItemConfig.row) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return void 0; // this should never be reached
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'dashboard-page',
|
selector: 'dashboard-page',
|
||||||
templateUrl: decodeURI(require.toUrl('sql/parts/dashboard/common/dashboardPage.component.html'))
|
templateUrl: decodeURI(require.toUrl('sql/parts/dashboard/common/dashboardPage.component.html'))
|
||||||
@@ -90,43 +59,31 @@ function configSorter(a, b): number {
|
|||||||
export abstract class DashboardPage extends Disposable implements OnDestroy {
|
export abstract class DashboardPage extends Disposable implements OnDestroy {
|
||||||
|
|
||||||
protected SKELETON_WIDTH = 5;
|
protected SKELETON_WIDTH = 5;
|
||||||
protected widgets: Array<WidgetConfig> = [];
|
protected tabs: Array<TabConfig> = [];
|
||||||
protected gridConfig: NgGridConfig = {
|
|
||||||
'margins': [10], // The size of the margins of each item. Supports up to four values in the same way as CSS margins. Can be updated using setMargins()
|
|
||||||
'draggable': false, // Whether the items can be dragged. Can be updated using enableDrag()/disableDrag()
|
|
||||||
'resizable': false, // Whether the items can be resized. Can be updated using enableResize()/disableResize()
|
|
||||||
'max_cols': this.SKELETON_WIDTH, // The maximum number of columns allowed. Set to 0 for infinite. Cannot be used with max_rows
|
|
||||||
'max_rows': 0, // The maximum number of rows allowed. Set to 0 for infinite. Cannot be used with max_cols
|
|
||||||
'visible_cols': 0, // The number of columns shown on screen when auto_resize is set to true. Set to 0 to not auto_resize. Will be overriden by max_cols
|
|
||||||
'visible_rows': 0, // The number of rows shown on screen when auto_resize is set to true. Set to 0 to not auto_resize. Will be overriden by max_rows
|
|
||||||
'min_cols': 0, // The minimum number of columns allowed. Can be any number greater than or equal to 1.
|
|
||||||
'min_rows': 0, // The minimum number of rows allowed. Can be any number greater than or equal to 1.
|
|
||||||
'col_width': 250, // The width of each column
|
|
||||||
'row_height': 250, // The height of each row
|
|
||||||
'cascade': 'left', // The direction to cascade grid items ('up', 'right', 'down', 'left')
|
|
||||||
'min_width': 100, // The minimum width of an item. If greater than col_width, this will update the value of min_cols
|
|
||||||
'min_height': 100, // The minimum height of an item. If greater than row_height, this will update the value of min_rows
|
|
||||||
'fix_to_grid': false, // Fix all item movements to the grid
|
|
||||||
'auto_style': true, // Automatically add required element styles at run-time
|
|
||||||
'auto_resize': false, // Automatically set col_width/row_height so that max_cols/max_rows fills the screen. Only has effect is max_cols or max_rows is set
|
|
||||||
'maintain_ratio': false, // Attempts to maintain aspect ratio based on the colWidth/rowHeight values set in the config
|
|
||||||
'prefer_new': false, // When adding new items, will use that items position ahead of existing items
|
|
||||||
'limit_to_screen': true, // When resizing the screen, with this true and auto_resize false, the grid will re-arrange to fit the screen size. Please note, at present this only works with cascade direction up.
|
|
||||||
};
|
|
||||||
private _originalConfig: WidgetConfig[];
|
private _originalConfig: WidgetConfig[];
|
||||||
private _editDispose: Array<IDisposable> = [];
|
|
||||||
private _scrollableElement: ScrollableElement;
|
private _scrollableElement: ScrollableElement;
|
||||||
|
|
||||||
private _widgetConfigLocation: string;
|
private _widgetConfigLocation: string;
|
||||||
private _propertiesConfigLocation: string;
|
private _propertiesConfigLocation: string;
|
||||||
|
|
||||||
|
protected panelActions: Action[];
|
||||||
|
private _tabsDispose: Array<IDisposable> = [];
|
||||||
|
private _pinnedTabs: Array<PinConfig> = [];
|
||||||
|
|
||||||
@ViewChild('properties') private _properties: DashboardWidgetWrapper;
|
@ViewChild('properties') private _properties: DashboardWidgetWrapper;
|
||||||
@ViewChild(NgGrid) private _grid: NgGrid;
|
|
||||||
@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;
|
||||||
@ViewChild('propertiesContainer', { read: ElementRef }) private _propertiesContainer: ElementRef;
|
@ViewChild('propertiesContainer', { read: ElementRef }) private _propertiesContainer: ElementRef;
|
||||||
@ViewChildren(DashboardWidgetWrapper) private _widgets: QueryList<DashboardWidgetWrapper>;
|
@ViewChildren(DashboardTab) private _tabs: QueryList<DashboardTab>;
|
||||||
@ViewChildren(NgGridItem) private _items: QueryList<NgGridItem>;
|
@ViewChild(PanelComponent) private _panel: PanelComponent;
|
||||||
|
|
||||||
|
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');
|
||||||
|
|
||||||
// a set of config modifiers
|
// a set of config modifiers
|
||||||
private readonly _configModifiers: Array<(item: Array<WidgetConfig>) => Array<WidgetConfig>> = [
|
private readonly _configModifiers: Array<(item: Array<WidgetConfig>) => Array<WidgetConfig>> = [
|
||||||
@@ -135,7 +92,7 @@ export abstract class DashboardPage extends Disposable implements OnDestroy {
|
|||||||
this.addProvider,
|
this.addProvider,
|
||||||
this.addEdition,
|
this.addEdition,
|
||||||
this.addContext,
|
this.addContext,
|
||||||
this.filterWidgets
|
this.filterConfigs
|
||||||
];
|
];
|
||||||
|
|
||||||
private readonly _gridModifiers: Array<(item: Array<WidgetConfig>) => Array<WidgetConfig>> = [
|
private readonly _gridModifiers: Array<(item: Array<WidgetConfig>) => Array<WidgetConfig>> = [
|
||||||
@@ -144,6 +101,7 @@ 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
|
||||||
) {
|
) {
|
||||||
@@ -156,7 +114,7 @@ export abstract class DashboardPage extends Disposable implements OnDestroy {
|
|||||||
} else {
|
} else {
|
||||||
let tempWidgets = this.dashboardService.getSettings<Array<WidgetConfig>>([this.context, 'widgets'].join('.'));
|
let tempWidgets = this.dashboardService.getSettings<Array<WidgetConfig>>([this.context, 'widgets'].join('.'));
|
||||||
this._widgetConfigLocation = 'default';
|
this._widgetConfigLocation = 'default';
|
||||||
this._originalConfig = objects.clone(tempWidgets);
|
this._originalConfig = objects.deepClone(tempWidgets);
|
||||||
let properties = this.getProperties();
|
let properties = this.getProperties();
|
||||||
this._configModifiers.forEach((cb) => {
|
this._configModifiers.forEach((cb) => {
|
||||||
tempWidgets = cb.apply(this, [tempWidgets]);
|
tempWidgets = cb.apply(this, [tempWidgets]);
|
||||||
@@ -165,8 +123,10 @@ export abstract class DashboardPage extends Disposable implements OnDestroy {
|
|||||||
this._gridModifiers.forEach(cb => {
|
this._gridModifiers.forEach(cb => {
|
||||||
tempWidgets = cb.apply(this, [tempWidgets]);
|
tempWidgets = cb.apply(this, [tempWidgets]);
|
||||||
});
|
});
|
||||||
this.widgets = tempWidgets;
|
|
||||||
this.propertiesWidget = properties ? properties[0] : undefined;
|
this.propertiesWidget = properties ? properties[0] : undefined;
|
||||||
|
|
||||||
|
this.createTabs(tempWidgets);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,15 +149,18 @@ export abstract class DashboardPage extends Disposable implements OnDestroy {
|
|||||||
container.appendChild(this._scrollableElement.getDomNode());
|
container.appendChild(this._scrollableElement.getDomNode());
|
||||||
let initalHeight = getContentHeight(scrollable);
|
let initalHeight = getContentHeight(scrollable);
|
||||||
this._scrollableElement.setScrollDimensions({
|
this._scrollableElement.setScrollDimensions({
|
||||||
scrollHeight: getContentHeight(scrollable),
|
scrollHeight: Math.max(getContentHeight(scrollable), getContentHeight(container)),
|
||||||
height: getContentHeight(container)
|
height: getContentHeight(container)
|
||||||
});
|
});
|
||||||
|
|
||||||
this._register(addDisposableListener(window, EventType.RESIZE, () => {
|
this._register(addDisposableListener(window, EventType.RESIZE, () => {
|
||||||
this._scrollableElement.setScrollDimensions({
|
// Todo: Need to set timeout because we have to make sure that the grids have already rearraged before the getContentHeight gets called.
|
||||||
scrollHeight: getContentHeight(scrollable),
|
setTimeout(() => {
|
||||||
height: getContentHeight(container)
|
this._scrollableElement.setScrollDimensions({
|
||||||
});
|
scrollHeight: Math.max(getContentHeight(scrollable), getContentHeight(container)),
|
||||||
|
height: getContentHeight(container)
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// unforunately because of angular rendering behavior we need to do a double check to make sure nothing changed after this point
|
// unforunately because of angular rendering behavior we need to do a double check to make sure nothing changed after this point
|
||||||
@@ -205,13 +168,147 @@ export abstract class DashboardPage extends Disposable implements OnDestroy {
|
|||||||
let currentheight = getContentHeight(scrollable);
|
let currentheight = getContentHeight(scrollable);
|
||||||
if (initalHeight !== currentheight) {
|
if (initalHeight !== currentheight) {
|
||||||
this._scrollableElement.setScrollDimensions({
|
this._scrollableElement.setScrollDimensions({
|
||||||
scrollHeight: getContentHeight(scrollable),
|
scrollHeight: Math.max(getContentHeight(scrollable), getContentHeight(container)),
|
||||||
height: getContentHeight(container)
|
height: getContentHeight(container)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private createTabs(homeWidgets: WidgetConfig[]) {
|
||||||
|
// Clear all tabs
|
||||||
|
this.tabs = [];
|
||||||
|
this._pinnedTabs = [];
|
||||||
|
this._tabsDispose.forEach(i => i.dispose());
|
||||||
|
this._tabsDispose = [];
|
||||||
|
|
||||||
|
// Create home tab
|
||||||
|
let homeTab: TabConfig = {
|
||||||
|
id: 'homeTab',
|
||||||
|
publisher: undefined,
|
||||||
|
title: this.homeTabTitle,
|
||||||
|
content: { 'widgets-tab': homeWidgets },
|
||||||
|
context: this.context,
|
||||||
|
originalConfig: this._originalConfig,
|
||||||
|
editable: true,
|
||||||
|
canClose: false,
|
||||||
|
actions: []
|
||||||
|
};
|
||||||
|
this.addNewTab(homeTab);
|
||||||
|
this._panel.selectTab(homeTab.id);
|
||||||
|
|
||||||
|
let allTabs = this.filterConfigs(dashboardRegistry.tabs);
|
||||||
|
|
||||||
|
// Load always show tabs
|
||||||
|
let alwaysShowTabs = allTabs.filter(tab => tab.alwaysShow);
|
||||||
|
this.loadNewTabs(alwaysShowTabs);
|
||||||
|
|
||||||
|
// Load pinned tabs
|
||||||
|
this._pinnedTabs = this.dashboardService.getSettings<Array<PinConfig>>([this.context, 'tabs'].join('.'));
|
||||||
|
let pinnedDashboardTabs: IDashboardTab[] = [];
|
||||||
|
this._pinnedTabs.forEach(pinnedTab => {
|
||||||
|
let tab = allTabs.find(i => i.id === pinnedTab.tabId);
|
||||||
|
if (tab) {
|
||||||
|
pinnedDashboardTabs.push(tab);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.loadNewTabs(pinnedDashboardTabs);
|
||||||
|
|
||||||
|
// Set panel actions
|
||||||
|
let openedTabs = [...pinnedDashboardTabs, ...alwaysShowTabs];
|
||||||
|
let addNewTabAction = this.dashboardService.instantiationService.createInstance(AddFeatureTabAction, allTabs, openedTabs, this.dashboardService.getUnderlyingUri());
|
||||||
|
this._tabsDispose.push(addNewTabAction);
|
||||||
|
this.panelActions = [addNewTabAction];
|
||||||
|
this._cd.detectChanges();
|
||||||
|
|
||||||
|
this._tabsDispose.push(this.dashboardService.onPinUnpinTab(e => {
|
||||||
|
if (e.isPinned) {
|
||||||
|
this._pinnedTabs.push(e);
|
||||||
|
} else {
|
||||||
|
let index = this._pinnedTabs.findIndex(i => i.tabId === e.tabId);
|
||||||
|
this._pinnedTabs.splice(index, 1);
|
||||||
|
}
|
||||||
|
this.rewriteConfig();
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._tabsDispose.push(this.dashboardService.onAddNewTabs(e => {
|
||||||
|
this.loadNewTabs(e);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private rewriteConfig(): void {
|
||||||
|
let writeableConfig = objects.deepClone(this._pinnedTabs);
|
||||||
|
|
||||||
|
writeableConfig.forEach(i => {
|
||||||
|
delete i.isPinned;
|
||||||
|
});
|
||||||
|
let target: ConfigurationTarget = ConfigurationTarget.USER;
|
||||||
|
this.dashboardService.writeSettings([this.context, 'tabs'].join('.'), writeableConfig, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
private loadNewTabs(dashboardTabs: IDashboardTab[]) {
|
||||||
|
if (dashboardTabs && dashboardTabs.length > 0) {
|
||||||
|
let selectedTabs = dashboardTabs.map(v => {
|
||||||
|
|
||||||
|
if (Object.keys(v.content).length !== 1) {
|
||||||
|
error('Exactly 1 widget must be defined per space');
|
||||||
|
}
|
||||||
|
|
||||||
|
let key = Object.keys(v.content)[0];
|
||||||
|
if (key === WIDGETS_TABS) {
|
||||||
|
let configs = <WidgetConfig[]>Object.values(v.content)[0];
|
||||||
|
this._configModifiers.forEach(cb => {
|
||||||
|
configs = cb.apply(this, [configs]);
|
||||||
|
});
|
||||||
|
this._gridModifiers.forEach(cb => {
|
||||||
|
configs = cb.apply(this, [configs]);
|
||||||
|
});
|
||||||
|
return { id: v.id, title: v.title, content: { 'widgets-tab': configs }, alwaysShow: v.alwaysShow };
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}).map(v => {
|
||||||
|
let actions = [];
|
||||||
|
if (!v.alwaysShow) {
|
||||||
|
let pinnedTab = this._pinnedTabs.find(i => i.tabId === v.id);
|
||||||
|
actions.push(this.dashboardService.instantiationService.createInstance(PinUnpinTabAction, v.id, this.dashboardService.getUnderlyingUri(), !!pinnedTab));
|
||||||
|
}
|
||||||
|
|
||||||
|
let config = v as TabConfig;
|
||||||
|
config.context = this.context;
|
||||||
|
config.editable = false;
|
||||||
|
config.canClose = true;
|
||||||
|
config.actions = actions;
|
||||||
|
this.addNewTab(config);
|
||||||
|
return config;
|
||||||
|
});
|
||||||
|
|
||||||
|
// put this immediately on the stack so that is ran *after* the tab is rendered
|
||||||
|
setTimeout(() => {
|
||||||
|
this._panel.selectTab(selectedTabs.pop().id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private getContentType(tab: TabConfig): string {
|
||||||
|
return tab.content ? Object.keys(tab.content)[0] : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
private addNewTab(tab: TabConfig): void {
|
||||||
|
let existedTab = this.tabs.find(i => i.id === tab.id);
|
||||||
|
if (!existedTab) {
|
||||||
|
this.tabs.push(tab);
|
||||||
|
this._cd.detectChanges();
|
||||||
|
let tabComponents = this._tabs.find(i => i.id === tab.id);
|
||||||
|
this._register(tabComponents.onResize(() => {
|
||||||
|
this._scrollableElement.setScrollDimensions({
|
||||||
|
scrollHeight: Math.max(getContentHeight(this._scrollable.nativeElement), getContentHeight(this._scrollContainer.nativeElement)),
|
||||||
|
height: getContentHeight(this._scrollContainer.nativeElement)
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private updateTheme(theme: IColorTheme): void {
|
private updateTheme(theme: IColorTheme): void {
|
||||||
let el = this._propertiesContainer.nativeElement as HTMLElement;
|
let el = this._propertiesContainer.nativeElement as HTMLElement;
|
||||||
let border = theme.getColor(colors.contrastBorder, true);
|
let border = theme.getColor(colors.contrastBorder, true);
|
||||||
@@ -240,14 +337,18 @@ export abstract class DashboardPage extends Disposable implements OnDestroy {
|
|||||||
* Returns a filtered version of the widgets passed based on edition and provider
|
* Returns a filtered version of the widgets passed based on edition and provider
|
||||||
* @param config widgets to filter
|
* @param config widgets to filter
|
||||||
*/
|
*/
|
||||||
private filterWidgets(config: WidgetConfig[]): Array<WidgetConfig> {
|
private filterConfigs<T extends { provider?: string | string[], edition?: number | number[] }>(config: T[]): Array<T> {
|
||||||
let connectionInfo: ConnectionManagementInfo = this.dashboardService.connectionManagementService.connectionInfo;
|
let connectionInfo: ConnectionManagementInfo = this.dashboardService.connectionManagementService.connectionInfo;
|
||||||
let edition = connectionInfo.serverInfo.engineEditionId;
|
let edition = connectionInfo.serverInfo.engineEditionId;
|
||||||
let provider = connectionInfo.providerId;
|
let provider = connectionInfo.providerId;
|
||||||
|
|
||||||
// filter by provider
|
// filter by provider
|
||||||
return config.filter((item) => {
|
return config.filter((item) => {
|
||||||
return this.stringCompare(item.provider, provider);
|
if (item.provider) {
|
||||||
|
return this.stringCompare(item.provider, provider);
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}).filter((item) => {
|
}).filter((item) => {
|
||||||
if (item.edition) {
|
if (item.edition) {
|
||||||
if (edition) {
|
if (edition) {
|
||||||
@@ -407,96 +508,42 @@ export abstract class DashboardPage extends Disposable implements OnDestroy {
|
|||||||
public refresh(refreshConfig: boolean = false): void {
|
public refresh(refreshConfig: boolean = false): void {
|
||||||
if (refreshConfig) {
|
if (refreshConfig) {
|
||||||
this.init();
|
this.init();
|
||||||
if (this._properties) {
|
this.refreshProperties();
|
||||||
this._properties.refresh();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (this._widgets) {
|
this.refreshProperties();
|
||||||
this._widgets.forEach(item => {
|
if (this._tabs) {
|
||||||
item.refresh();
|
this._tabs.forEach(tabContent => {
|
||||||
|
tabContent.refresh();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private refreshProperties(): void {
|
||||||
|
if (this._properties) {
|
||||||
|
this._properties.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public enableEdit(): void {
|
public enableEdit(): void {
|
||||||
if (this._grid.dragEnable) {
|
if (this._tabs) {
|
||||||
this._grid.disableDrag();
|
this._tabs.forEach(tabContent => {
|
||||||
this._grid.disableResize();
|
tabContent.enableEdit();
|
||||||
this._editDispose.forEach(i => i.dispose());
|
|
||||||
this._widgets.forEach(i => {
|
|
||||||
if (i.id) {
|
|
||||||
i.disableEdit();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this._editDispose = [];
|
|
||||||
} else {
|
|
||||||
this._grid.enableResize();
|
|
||||||
this._grid.enableDrag();
|
|
||||||
this._editDispose.push(this.dashboardService.onDeleteWidget(e => {
|
|
||||||
let index = this.widgets.findIndex(i => i.id === e);
|
|
||||||
this.widgets.splice(index, 1);
|
|
||||||
|
|
||||||
index = this._originalConfig.findIndex(i => i.id === e);
|
|
||||||
this._originalConfig.splice(index, 1);
|
|
||||||
|
|
||||||
this._rewriteConfig();
|
|
||||||
this._cd.detectChanges();
|
|
||||||
}));
|
|
||||||
this._editDispose.push(subscriptionToDisposable(this._grid.onResizeStop.subscribe((e: NgGridItem) => {
|
|
||||||
this._scrollableElement.setScrollDimensions({
|
|
||||||
scrollHeight: getContentHeight(this._scrollable.nativeElement),
|
|
||||||
height: getContentHeight(this._scrollContainer.nativeElement)
|
|
||||||
});
|
|
||||||
let event = e.getEventOutput();
|
|
||||||
let config = this._originalConfig.find(i => i.id === event.payload.id);
|
|
||||||
|
|
||||||
if (!config.gridItemConfig) {
|
|
||||||
config.gridItemConfig = {};
|
|
||||||
}
|
|
||||||
config.gridItemConfig.sizex = e.sizex;
|
|
||||||
config.gridItemConfig.sizey = e.sizey;
|
|
||||||
|
|
||||||
let component = this._widgets.find(i => i.id === event.payload.id);
|
|
||||||
|
|
||||||
component.layout();
|
|
||||||
this._rewriteConfig();
|
|
||||||
})));
|
|
||||||
this._editDispose.push(subscriptionToDisposable(this._grid.onDragStop.subscribe((e: NgGridItem) => {
|
|
||||||
this._scrollableElement.setScrollDimensions({
|
|
||||||
scrollHeight: getContentHeight(this._scrollable.nativeElement),
|
|
||||||
height: getContentHeight(this._scrollContainer.nativeElement)
|
|
||||||
});
|
|
||||||
let event = e.getEventOutput();
|
|
||||||
this._items.forEach(i => {
|
|
||||||
let config = this._originalConfig.find(j => j.id === i.getEventOutput().payload.id);
|
|
||||||
if ((config.gridItemConfig && config.gridItemConfig.col) || config.id === event.payload.id) {
|
|
||||||
if (!config.gridItemConfig) {
|
|
||||||
config.gridItemConfig = {};
|
|
||||||
}
|
|
||||||
config.gridItemConfig.col = i.col;
|
|
||||||
config.gridItemConfig.row = i.row;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this._originalConfig.sort(configSorter);
|
|
||||||
|
|
||||||
this._rewriteConfig();
|
|
||||||
})));
|
|
||||||
this._widgets.forEach(i => {
|
|
||||||
if (i.id) {
|
|
||||||
i.enableEdit();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _rewriteConfig(): void {
|
public handleTabChange(tab: TabComponent): void {
|
||||||
let writeableConfig = objects.clone(this._originalConfig);
|
let localtab = this._tabs.find(i => i.id === tab.identifier);
|
||||||
|
this._editEnabled.fire(localtab.editable);
|
||||||
|
this._cd.detectChanges();
|
||||||
|
localtab.layout();
|
||||||
|
}
|
||||||
|
|
||||||
writeableConfig.forEach(i => {
|
public handleTabClose(tab: TabComponent): void {
|
||||||
delete i.id;
|
let index = this.tabs.findIndex(i => i.id === tab.identifier);
|
||||||
});
|
this.tabs.splice(index, 1);
|
||||||
let target: ConfigurationTarget = ConfigurationTarget.USER;
|
this._cd.detectChanges();
|
||||||
this.dashboardService.writeSettings(this.context, writeableConfig, target);
|
this.bootstrapService.angularEventingService.sendAngularEvent(this.dashboardService.getUnderlyingUri(), AngularEventType.CLOSE_TAB, { id: tab.identifier });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,3 +8,8 @@ dashboard-page {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dashboard-page .monaco-scrollable-element {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|||||||
19
src/sql/parts/dashboard/common/dashboardPanel.css
Normal file
19
src/sql/parts/dashboard/common/dashboardPanel.css
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
panel.dashboard-panel > .tabbedPanel {
|
||||||
|
border-top-width: 0px;
|
||||||
|
border-top-style: solid;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
panel.dashboard-panel > .tabbedPanel > .title > .tabList .tab-header .tab > .tabLabel.active {
|
||||||
|
border-bottom: 0px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
panel.dashboard-panel > .tabbedPanel > .title > .tabList .tab-header {
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
95
src/sql/parts/dashboard/common/dashboardPanelStyles.ts
Normal file
95
src/sql/parts/dashboard/common/dashboardPanelStyles.ts
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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!./dashboardPanel';
|
||||||
|
|
||||||
|
import { registerThemingParticipant, ITheme, ICssStyleCollector } 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 } from 'vs/workbench/common/theme';
|
||||||
|
import { activeContrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||||
|
|
||||||
|
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||||
|
|
||||||
|
// Title Active
|
||||||
|
const tabActiveBackground = theme.getColor(TAB_ACTIVE_BACKGROUND);
|
||||||
|
const tabActiveForeground = theme.getColor(TAB_ACTIVE_FOREGROUND);
|
||||||
|
if (tabActiveBackground || tabActiveForeground) {
|
||||||
|
collector.addRule(`
|
||||||
|
panel.dashboard-panel > .tabbedPanel > .title > .tabList .tab:hover .tabLabel,
|
||||||
|
panel.dashboard-panel > .tabbedPanel > .title > .tabList .tab .tabLabel.active {
|
||||||
|
color: ${tabActiveForeground};
|
||||||
|
border-bottom: 0px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
panel.dashboard-panel > .tabbedPanel > .title > .tabList .tab-header.active {
|
||||||
|
background-color: ${tabActiveBackground};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeTabBorderColor = theme.getColor(TAB_ACTIVE_BORDER);
|
||||||
|
if (activeTabBorderColor) {
|
||||||
|
collector.addRule(`
|
||||||
|
panel.dashboard-panel > .tabbedPanel > .title > .tabList .tab-header.active {
|
||||||
|
box-shadow: ${activeTabBorderColor} 0 -1px inset;
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Title Inactive
|
||||||
|
const tabInactiveBackground = theme.getColor(TAB_INACTIVE_BACKGROUND);
|
||||||
|
const tabInactiveForeground = theme.getColor(TAB_INACTIVE_FOREGROUND);
|
||||||
|
if (tabInactiveBackground || tabInactiveForeground) {
|
||||||
|
collector.addRule(`
|
||||||
|
panel.dashboard-panel > .tabbedPanel > .title > .tabList .tab .tabLabel {
|
||||||
|
color: ${tabInactiveForeground};
|
||||||
|
}
|
||||||
|
|
||||||
|
panel.dashboard-panel > .tabbedPanel > .title > .tabList .tab-header {
|
||||||
|
background-color: ${tabInactiveBackground};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panel title background
|
||||||
|
const panelTitleBackground = theme.getColor(EDITOR_GROUP_HEADER_TABS_BACKGROUND);
|
||||||
|
if (panelTitleBackground) {
|
||||||
|
collector.addRule(`
|
||||||
|
panel.dashboard-panel > .tabbedPanel > .title {
|
||||||
|
background-color: ${panelTitleBackground};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panel title background
|
||||||
|
const tabBoarder = theme.getColor(TAB_BORDER);
|
||||||
|
if (tabBoarder) {
|
||||||
|
collector.addRule(`
|
||||||
|
panel.dashboard-panel > .tabbedPanel.horizontal > .title > .tabList .tab-header {
|
||||||
|
border-right-color: ${tabBoarder};
|
||||||
|
}
|
||||||
|
|
||||||
|
panel.dashboard-panel > .tabbedPanel.vertical > .title > .tabList .tab-header {
|
||||||
|
border-bottom-color: ${tabBoarder};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Styling with Outline color (e.g. high contrast theme)
|
||||||
|
const outline = theme.getColor(activeContrastBorder);
|
||||||
|
if (outline) {
|
||||||
|
collector.addRule(`
|
||||||
|
panel.dashboard-panel > .tabbedPanel > .title {
|
||||||
|
border-bottom-color: ${tabBoarder};
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
border-bottom-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
panel.dashboard-panel > .tabbedPanel.vertical > .title {
|
||||||
|
border-right-color: ${tabBoarder};
|
||||||
|
border-right-width: 1px;
|
||||||
|
border-right-style: solid;
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
});
|
||||||
116
src/sql/parts/dashboard/common/dashboardTab.contribution.ts
Normal file
116
src/sql/parts/dashboard/common/dashboardTab.contribution.ts
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
import { IExtensionPointUser, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry';
|
||||||
|
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||||
|
import { localize } from 'vs/nls';
|
||||||
|
|
||||||
|
import { registerTab, generateTabContentSchemaProperties } from 'sql/platform/dashboard/common/dashboardRegistry';
|
||||||
|
|
||||||
|
export interface IDashboardTabContrib {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
content: object;
|
||||||
|
description?: string;
|
||||||
|
provider?: string | string[];
|
||||||
|
edition?: number | number[];
|
||||||
|
alwaysShow?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tabSchema: IJSONSchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string',
|
||||||
|
description: localize('sqlops.extension.contributes.dashboard.tab.id', "Unique identifier for this tab. Will be passed to the extension for any requests.")
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: 'string',
|
||||||
|
description: localize('sqlops.extension.contributes.dashboard.tab.title', "Title of the tab to show the user.")
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
description: localize('sqlops.extension.contributes.dashboard.tab.description', "Description of this tab that will be shown to the user."),
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
provider: {
|
||||||
|
description: localize('sqlops.extension.contributes.dashboard.tab.provider', "Providers for which this tab should be allowed for."),
|
||||||
|
anyOf: [
|
||||||
|
{
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
edition: {
|
||||||
|
description: localize('sqlops.extension.contributes.dashboard.tab.edition', "Editions for which this tab should be allowed for."),
|
||||||
|
anyOf: [
|
||||||
|
{
|
||||||
|
type: 'number'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'number'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
description: localize('sqlops.extension.contributes.dashboard.tab.content', "The content that will be displayed in this tab."),
|
||||||
|
type: 'object',
|
||||||
|
properties: generateTabContentSchemaProperties()
|
||||||
|
},
|
||||||
|
alwaysShow: {
|
||||||
|
description: localize('sqlops.extension.contributes.dashboard.tab.alwaysShow', "Whether or not this tab should always be shown or only when the user adds it."),
|
||||||
|
type: 'boolean'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const tabContributionSchema: IJSONSchema = {
|
||||||
|
description: localize('sqlops.extension.contributes.tabs', "Contributes a single or multiple tabs for users to add to their dashboard."),
|
||||||
|
oneOf: [
|
||||||
|
tabSchema,
|
||||||
|
{
|
||||||
|
type: 'array',
|
||||||
|
items: tabSchema
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
ExtensionsRegistry.registerExtensionPoint<IDashboardTabContrib | IDashboardTabContrib[]>('dashboard.tabs', [], tabContributionSchema).setHandler(extensions => {
|
||||||
|
|
||||||
|
function handleCommand(tab: IDashboardTabContrib, extension: IExtensionPointUser<any>) {
|
||||||
|
let { description, content, title, edition, provider, id, alwaysShow } = tab;
|
||||||
|
alwaysShow = alwaysShow || false;
|
||||||
|
let publisher = extension.description.publisher;
|
||||||
|
if (!title) {
|
||||||
|
extension.collector.error('No title specified for extension.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!description) {
|
||||||
|
extension.collector.warn('No description specified to show.');
|
||||||
|
}
|
||||||
|
if (!content) {
|
||||||
|
extension.collector.warn('No content specified to show.');
|
||||||
|
}
|
||||||
|
registerTab({ description, title, content, edition, provider, id, alwaysShow, publisher });
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let extension of extensions) {
|
||||||
|
const { value } = extension;
|
||||||
|
if (Array.isArray<IDashboardTabContrib>(value)) {
|
||||||
|
for (let command of value) {
|
||||||
|
handleCommand(command, extension);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handleCommand(value, extension);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -6,6 +6,7 @@ import { InjectionToken, OnDestroy } from '@angular/core';
|
|||||||
import { NgGridItemConfig } from 'angular2-grid';
|
import { NgGridItemConfig } from 'angular2-grid';
|
||||||
import { Action } from 'vs/base/common/actions';
|
import { Action } from 'vs/base/common/actions';
|
||||||
import { Disposable } from 'vs/base/common/lifecycle';
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
|
import { IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
|
||||||
|
|
||||||
export interface IDashboardWidget {
|
export interface IDashboardWidget {
|
||||||
actions: Array<Action>;
|
actions: Array<Action>;
|
||||||
@@ -32,6 +33,19 @@ export interface WidgetConfig {
|
|||||||
padding?: string;
|
padding?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TabConfig extends IDashboardTab {
|
||||||
|
context: string;
|
||||||
|
originalConfig: Array<WidgetConfig>;
|
||||||
|
editable: boolean;
|
||||||
|
canClose: boolean;
|
||||||
|
actions?: Array<Action>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PinConfig {
|
||||||
|
tabId: string;
|
||||||
|
isPinned?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export abstract class DashboardWidget extends Disposable implements OnDestroy {
|
export abstract class DashboardWidget extends Disposable implements OnDestroy {
|
||||||
protected _config: WidgetConfig;
|
protected _config: WidgetConfig;
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<div style="display: flex; flex-flow: column; overflow: hidden; height: 100%; width: 100%">
|
<div style="display: flex; flex-flow: column; overflow: hidden; height: 100%; width: 100%">
|
||||||
|
|
||||||
<div #header>
|
<div #header>
|
||||||
<div *ngIf="_config.name || _config.loadedIcon || _actions" style="display: flex; flex: 0 0; padding: 3px 0 3px 0; flex-direction: row-reverse; justify-content: space-between">
|
<div style="display: flex; flex: 0 0; padding: 3px 0 3px 0; flex-direction: row-reverse; justify-content: space-between">
|
||||||
<span #actionbar style="flex: 0 0 auto; align-self: end"></span>
|
<span #actionbar style="flex: 0 0 auto; align-self: end"></span>
|
||||||
<span *ngIf="_config.name" style="margin-left: 5px">{{_config.name}}</span>
|
<span *ngIf="_config.name" style="margin-left: 5px">{{_config.name}}</span>
|
||||||
<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>
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import { PropertiesWidgetComponent } from 'sql/parts/dashboard/widgets/propertie
|
|||||||
import { ExplorerWidget } from 'sql/parts/dashboard/widgets/explorer/explorerWidget.component';
|
import { ExplorerWidget } from 'sql/parts/dashboard/widgets/explorer/explorerWidget.component';
|
||||||
import { TasksWidget } from 'sql/parts/dashboard/widgets/tasks/tasksWidget.component';
|
import { TasksWidget } from 'sql/parts/dashboard/widgets/tasks/tasksWidget.component';
|
||||||
import { InsightsWidget } from 'sql/parts/dashboard/widgets/insights/insightsWidget.component';
|
import { InsightsWidget } from 'sql/parts/dashboard/widgets/insights/insightsWidget.component';
|
||||||
|
import { WebviewWidget } from 'sql/parts/dashboard/widgets/webview/webviewWidget.component';
|
||||||
|
|
||||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||||
|
|
||||||
@@ -36,7 +37,8 @@ const componentMap: { [x: string]: Type<IDashboardWidget> } = {
|
|||||||
'properties-widget': PropertiesWidgetComponent,
|
'properties-widget': PropertiesWidgetComponent,
|
||||||
'explorer-widget': ExplorerWidget,
|
'explorer-widget': ExplorerWidget,
|
||||||
'tasks-widget': TasksWidget,
|
'tasks-widget': TasksWidget,
|
||||||
'insights-widget': InsightsWidget
|
'insights-widget': InsightsWidget,
|
||||||
|
'webview-widget': WebviewWidget
|
||||||
};
|
};
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
|
import Event from 'vs/base/common/event';
|
||||||
|
|
||||||
export enum Conditional {
|
export enum Conditional {
|
||||||
'equals',
|
'equals',
|
||||||
'notEquals',
|
'notEquals',
|
||||||
@@ -11,4 +14,15 @@ export enum Conditional {
|
|||||||
'lessThanOrEquals',
|
'lessThanOrEquals',
|
||||||
'lessThan',
|
'lessThan',
|
||||||
'always'
|
'always'
|
||||||
};
|
}
|
||||||
|
|
||||||
|
export abstract class DashboardTab extends Disposable {
|
||||||
|
public abstract layout(): void;
|
||||||
|
public abstract readonly id: string;
|
||||||
|
public abstract readonly editable: boolean;
|
||||||
|
public abstract refresh(): void;
|
||||||
|
public abstract readonly onResize: Event<void>;
|
||||||
|
public enableEdit(): void {
|
||||||
|
// no op
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
|||||||
@ViewChild('header', { read: ElementRef }) private header: ElementRef;
|
@ViewChild('header', { read: ElementRef }) private header: ElementRef;
|
||||||
@ViewChild('actionBar', { read: ElementRef }) private actionbarContainer: ElementRef;
|
@ViewChild('actionBar', { read: ElementRef }) private actionbarContainer: ElementRef;
|
||||||
private actionbar: ActionBar;
|
private actionbar: ActionBar;
|
||||||
|
private editAction: EditDashboardAction;
|
||||||
|
private editDisposable: IDisposable;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(forwardRef(() => DashboardServiceInterface)) private _bootstrapService: DashboardServiceInterface,
|
@Inject(forwardRef(() => DashboardServiceInterface)) private _bootstrapService: DashboardServiceInterface,
|
||||||
@@ -48,7 +50,8 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
|||||||
icon: true,
|
icon: true,
|
||||||
label: false,
|
label: false,
|
||||||
});
|
});
|
||||||
this.actionbar.push(new EditDashboardAction(this.edit, this), {
|
this.editAction = new EditDashboardAction(this.edit, this);
|
||||||
|
this.actionbar.push(this.editAction, {
|
||||||
icon: true,
|
icon: true,
|
||||||
label: false,
|
label: false,
|
||||||
});
|
});
|
||||||
@@ -72,7 +75,11 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onActivate(page: DashboardPage) {
|
onActivate(page: DashboardPage) {
|
||||||
|
if (this.editDisposable) {
|
||||||
|
this.editDisposable.dispose();
|
||||||
|
}
|
||||||
this._currentPage = page;
|
this._currentPage = page;
|
||||||
|
this.editDisposable = page.editEnabled(e => this.editEnabled = e, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh(): void {
|
refresh(): void {
|
||||||
@@ -84,4 +91,8 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
|||||||
edit(): void {
|
edit(): void {
|
||||||
this._currentPage.enableEdit();
|
this._currentPage.enableEdit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set editEnabled(val: boolean) {
|
||||||
|
this.editAction.enabled = val;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,9 +27,14 @@ import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost
|
|||||||
/* Base Components */
|
/* Base Components */
|
||||||
import { DashboardComponent, DASHBOARD_SELECTOR } from 'sql/parts/dashboard/dashboard.component';
|
import { DashboardComponent, DASHBOARD_SELECTOR } from 'sql/parts/dashboard/dashboard.component';
|
||||||
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/common/dashboardWidgetWrapper.component';
|
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/common/dashboardWidgetWrapper.component';
|
||||||
|
import { DashboardWidgetTab } from 'sql/parts/dashboard/tabs/dashboardWidgetTab.component';
|
||||||
|
import { DashboardWebviewTab } from 'sql/parts/dashboard/tabs/dashboardWebviewTab.component';
|
||||||
import { BreadcrumbComponent } from 'sql/base/browser/ui/breadcrumb/breadcrumb.component';
|
import { BreadcrumbComponent } from 'sql/base/browser/ui/breadcrumb/breadcrumb.component';
|
||||||
import { IBreadcrumbService } from 'sql/base/browser/ui/breadcrumb/interfaces';
|
import { IBreadcrumbService } from 'sql/base/browser/ui/breadcrumb/interfaces';
|
||||||
let baseComponents = [DashboardComponent, DashboardWidgetWrapper, ComponentHostDirective, BreadcrumbComponent];
|
let baseComponents = [DashboardComponent, DashboardWidgetWrapper, DashboardWebviewTab, DashboardWidgetTab, ComponentHostDirective, BreadcrumbComponent];
|
||||||
|
|
||||||
|
/* Panel */
|
||||||
|
import { PanelModule } from 'sql/base/browser/ui/panel/panel.module';
|
||||||
|
|
||||||
/* Pages */
|
/* Pages */
|
||||||
import { ServerDashboardPage } from 'sql/parts/dashboard/pages/serverDashboardPage.component';
|
import { ServerDashboardPage } from 'sql/parts/dashboard/pages/serverDashboardPage.component';
|
||||||
@@ -41,7 +46,14 @@ import { PropertiesWidgetComponent } from 'sql/parts/dashboard/widgets/propertie
|
|||||||
import { ExplorerWidget } from 'sql/parts/dashboard/widgets/explorer/explorerWidget.component';
|
import { ExplorerWidget } from 'sql/parts/dashboard/widgets/explorer/explorerWidget.component';
|
||||||
import { TasksWidget } from 'sql/parts/dashboard/widgets/tasks/tasksWidget.component';
|
import { TasksWidget } from 'sql/parts/dashboard/widgets/tasks/tasksWidget.component';
|
||||||
import { InsightsWidget } from 'sql/parts/dashboard/widgets/insights/insightsWidget.component';
|
import { InsightsWidget } from 'sql/parts/dashboard/widgets/insights/insightsWidget.component';
|
||||||
let widgetComponents = [PropertiesWidgetComponent, ExplorerWidget, TasksWidget, InsightsWidget];
|
import { WebviewWidget } from 'sql/parts/dashboard/widgets/webview/webviewWidget.component';
|
||||||
|
let widgetComponents = [
|
||||||
|
PropertiesWidgetComponent,
|
||||||
|
ExplorerWidget,
|
||||||
|
TasksWidget,
|
||||||
|
InsightsWidget,
|
||||||
|
WebviewWidget
|
||||||
|
];
|
||||||
|
|
||||||
/* Insights */
|
/* Insights */
|
||||||
let insightComponents = Registry.as<IInsightRegistry>(Extensions.InsightContribution).getAllCtors();
|
let insightComponents = Registry.as<IInsightRegistry>(Extensions.InsightContribution).getAllCtors();
|
||||||
@@ -78,7 +90,8 @@ const appRoutes: Routes = [
|
|||||||
FormsModule,
|
FormsModule,
|
||||||
NgGridModule,
|
NgGridModule,
|
||||||
ChartsModule,
|
ChartsModule,
|
||||||
RouterModule.forRoot(appRoutes)
|
RouterModule.forRoot(appRoutes),
|
||||||
|
PanelModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: APP_BASE_HREF, useValue: '/' },
|
{ provide: APP_BASE_HREF, useValue: '/' },
|
||||||
|
|||||||
@@ -4,18 +4,21 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
import { Registry } from 'vs/platform/registry/common/platform';
|
import { Registry } from 'vs/platform/registry/common/platform';
|
||||||
import { IConfigurationRegistry, Extensions, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry';
|
import { IConfigurationRegistry, Extensions, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry';
|
||||||
import { DATABASE_DASHBOARD_SETTING, DATABASE_DASHBOARD_PROPERTIES, databaseDashboardSettingSchema, databaseDashboardPropertiesSchema } from 'sql/parts/dashboard/pages/databaseDashboardPage.contribution';
|
import { DASHBOARD_CONFIG_ID } from 'sql/parts/dashboard/pages/dashboardPageContribution';
|
||||||
import { SERVER_DASHBOARD_SETTING, SERVER_DASHBOARD_PROPERTIES, serverDashboardSettingSchema, serverDashboardPropertiesSchema } from 'sql/parts/dashboard/pages/serverDashboardPage.contribution';
|
import { DATABASE_DASHBOARD_SETTING, DATABASE_DASHBOARD_PROPERTIES, DATABASE_DASHBOARD_TABS, databaseDashboardSettingSchema, databaseDashboardPropertiesSchema, databaseDashboardTabsSchema } from 'sql/parts/dashboard/pages/databaseDashboardPage.contribution';
|
||||||
|
import { SERVER_DASHBOARD_SETTING, SERVER_DASHBOARD_PROPERTIES, SERVER_DASHBOARD_TABS, serverDashboardSettingSchema, serverDashboardPropertiesSchema, serverDashboardTabsSchema } from 'sql/parts/dashboard/pages/serverDashboardPage.contribution';
|
||||||
|
|
||||||
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
|
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
|
||||||
const dashboardConfig: IConfigurationNode = {
|
const dashboardConfig: IConfigurationNode = {
|
||||||
id: 'Dashboard',
|
id: DASHBOARD_CONFIG_ID,
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
[DATABASE_DASHBOARD_PROPERTIES]: databaseDashboardPropertiesSchema,
|
[DATABASE_DASHBOARD_PROPERTIES]: databaseDashboardPropertiesSchema,
|
||||||
[SERVER_DASHBOARD_PROPERTIES]: serverDashboardPropertiesSchema,
|
[SERVER_DASHBOARD_PROPERTIES]: serverDashboardPropertiesSchema,
|
||||||
[DATABASE_DASHBOARD_SETTING]: databaseDashboardSettingSchema,
|
[DATABASE_DASHBOARD_SETTING]: databaseDashboardSettingSchema,
|
||||||
[SERVER_DASHBOARD_SETTING]: serverDashboardSettingSchema
|
[SERVER_DASHBOARD_SETTING]: serverDashboardSettingSchema,
|
||||||
|
[DATABASE_DASHBOARD_TABS]: databaseDashboardTabsSchema,
|
||||||
|
[SERVER_DASHBOARD_TABS]: serverDashboardTabsSchema
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
15
src/sql/parts/dashboard/newDashboardTabDialog/interface.ts
Normal file
15
src/sql/parts/dashboard/newDashboardTabDialog/interface.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
import { IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
|
||||||
|
|
||||||
|
export const INewDashboardTabDialogService = createDecorator<INewDashboardTabDialogService>('addNewDashboardTabService');
|
||||||
|
export interface INewDashboardTabDialogService {
|
||||||
|
_serviceBrand: any;
|
||||||
|
showDialog(dashboardTabs: Array<IDashboardTab>, openedTabs: Array<IDashboardTab>, uri: string): void;
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
.extension-view .monaco-split-view .split-view-view .header {
|
||||||
|
padding-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.extension-view .split-view-view .header .title {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.extension-view .split-view-view .header .count-badge-wrapper {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.extension-view .extensionTab-view .list-row {
|
||||||
|
padding: 15px;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.extension-view .extensionTab-view .list-row .extension-status-icon {
|
||||||
|
flex: 0 0 20px;
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.extension-view .extensionTab-view .list-row.extensionTab-list .extension-details {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.extension-view .extensionTab-view .list-row.extensionTab-list .extension-details .title {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 700;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.extension-view .extensionTab-view .list-row.extensionTab-list .extension-details .description {
|
||||||
|
font-size: 13px;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.extension-view .extensionTab-view .list-row.extensionTab-list .extension-details .publisher {
|
||||||
|
font-size: 90%;
|
||||||
|
padding-right: 6px;
|
||||||
|
opacity: .6;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
@@ -0,0 +1,241 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import 'vs/css!sql/media/icons/common-icons';
|
||||||
|
import 'vs/css!./media/newDashboardTabDialog';
|
||||||
|
import * as DOM from 'vs/base/browser/dom';
|
||||||
|
import { List } from 'vs/base/browser/ui/list/listWidget';
|
||||||
|
import { IListService, ListService } from 'vs/platform/list/browser/listService';
|
||||||
|
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||||
|
import Event, { Emitter } from 'vs/base/common/event';
|
||||||
|
import { localize } from 'vs/nls';
|
||||||
|
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||||
|
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||||
|
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||||
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||||
|
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||||
|
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||||
|
import { IDelegate, IRenderer } from 'vs/base/browser/ui/list/list';
|
||||||
|
|
||||||
|
import { Button } from 'sql/base/browser/ui/button/button';
|
||||||
|
import { Modal } from 'sql/base/browser/ui/modal/modal';
|
||||||
|
import { attachModalDialogStyler, attachButtonStyler } from 'sql/common/theme/styler';
|
||||||
|
import { FixedListView } from 'sql/platform/views/fixedListView';
|
||||||
|
import * as TelemetryKeys from 'sql/common/telemetryKeys';
|
||||||
|
import { SplitView } from 'sql/base/browser/ui/splitview/splitview';
|
||||||
|
import { NewDashboardTabViewModel, IDashboardUITab } from 'sql/parts/dashboard/newDashboardTabDialog/newDashboardTabViewModel';
|
||||||
|
import { IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
|
||||||
|
|
||||||
|
class ExtensionListDelegate implements IDelegate<IDashboardUITab> {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private _height: number
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public getHeight(element: IDashboardUITab): number {
|
||||||
|
return this._height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getTemplateId(element: IDashboardUITab): string {
|
||||||
|
return 'extensionListRenderer';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ExtensionListTemplate {
|
||||||
|
root: HTMLElement;
|
||||||
|
icon: HTMLElement;
|
||||||
|
title: HTMLElement;
|
||||||
|
description: HTMLElement;
|
||||||
|
publisher: HTMLElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExtensionListRenderer implements IRenderer<IDashboardUITab, ExtensionListTemplate> {
|
||||||
|
public static TEMPLATE_ID = 'extensionListRenderer';
|
||||||
|
private static readonly OPENED_TAB_CLASS = 'success';
|
||||||
|
private static readonly ICON_CLASS = 'extension-status-icon icon';
|
||||||
|
|
||||||
|
public get templateId(): string {
|
||||||
|
return ExtensionListRenderer.TEMPLATE_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public renderTemplate(container: HTMLElement): ExtensionListTemplate {
|
||||||
|
const tableTemplate: ExtensionListTemplate = Object.create(null);
|
||||||
|
tableTemplate.root = DOM.append(container, DOM.$('div.list-row.extensionTab-list'));
|
||||||
|
tableTemplate.icon = DOM.append(tableTemplate.root, DOM.$('div.icon'));
|
||||||
|
var titleContainer = DOM.append(tableTemplate.root, DOM.$('div.extension-details'));
|
||||||
|
tableTemplate.title = DOM.append(titleContainer, DOM.$('div.title'));
|
||||||
|
tableTemplate.description = DOM.append(titleContainer, DOM.$('div.description'));
|
||||||
|
tableTemplate.publisher = DOM.append(titleContainer, DOM.$('div.publisher'));
|
||||||
|
return tableTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public renderElement(dashboardTab: IDashboardUITab, index: number, templateData: ExtensionListTemplate): void {
|
||||||
|
templateData.icon.className = ExtensionListRenderer.ICON_CLASS;
|
||||||
|
if (dashboardTab.isOpened) {
|
||||||
|
templateData.icon.classList.add(ExtensionListRenderer.OPENED_TAB_CLASS);
|
||||||
|
}
|
||||||
|
templateData.title.innerText = dashboardTab.tabConfig.title;
|
||||||
|
templateData.description.innerText = dashboardTab.tabConfig.description;
|
||||||
|
templateData.publisher.innerText = dashboardTab.tabConfig.publisher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public disposeTemplate(template: ExtensionListTemplate): void {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NewDashboardTabDialog extends Modal {
|
||||||
|
public static EXTENSIONLIST_HEIGHT = 101;
|
||||||
|
|
||||||
|
// MEMBER VARIABLES ////////////////////////////////////////////////////
|
||||||
|
private _addNewTabButton: Button;
|
||||||
|
private _cancelButton: Button;
|
||||||
|
private _extensionList: List<IDashboardUITab>;
|
||||||
|
private _extensionTabView: FixedListView<IDashboardUITab>;
|
||||||
|
private _splitView: SplitView;
|
||||||
|
private _container: HTMLElement;
|
||||||
|
|
||||||
|
private _viewModel: NewDashboardTabViewModel;
|
||||||
|
|
||||||
|
// EVENTING ////////////////////////////////////////////////////////////
|
||||||
|
private _onAddTabs: Emitter<Array<IDashboardUITab>>;
|
||||||
|
public get onAddTabs(): Event<Array<IDashboardUITab>> { return this._onAddTabs.event; }
|
||||||
|
|
||||||
|
private _onCancel: Emitter<void>;
|
||||||
|
public get onCancel(): Event<void> { return this._onCancel.event; }
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@IPartService partService: IPartService,
|
||||||
|
@IThemeService private _themeService: IThemeService,
|
||||||
|
@IListService private _listService: IListService,
|
||||||
|
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||||
|
@IContextMenuService private _contextMenuService: IContextMenuService,
|
||||||
|
@IKeybindingService private _keybindingService: IKeybindingService,
|
||||||
|
@ITelemetryService telemetryService: ITelemetryService,
|
||||||
|
@IContextKeyService contextKeyService: IContextKeyService
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
localize('openInstalledFeatures', 'Open installed features'),
|
||||||
|
TelemetryKeys.AddNewDashboardTab,
|
||||||
|
partService,
|
||||||
|
telemetryService,
|
||||||
|
contextKeyService,
|
||||||
|
{ hasSpinner: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Setup the event emitters
|
||||||
|
this._onAddTabs = new Emitter<IDashboardUITab[]>();
|
||||||
|
this._onCancel = new Emitter<void>();
|
||||||
|
|
||||||
|
this._viewModel = new NewDashboardTabViewModel();
|
||||||
|
this._register(this._viewModel.updateTabListEvent(tabs => this.onUpdateTabList(tabs)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// MODAL OVERRIDE METHODS //////////////////////////////////////////////
|
||||||
|
protected layout(height?: number): void {
|
||||||
|
// Ignore height as it's a subcomponent being laid out
|
||||||
|
this._splitView.layout(DOM.getContentHeight(this._container));
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
super.render();
|
||||||
|
attachModalDialogStyler(this, this._themeService);
|
||||||
|
|
||||||
|
this._addNewTabButton = this.addFooterButton(localize('ok', 'OK'), () => this.addNewTabs());
|
||||||
|
this._cancelButton = this.addFooterButton(localize('cancel', 'Cancel'), () => this.cancel());
|
||||||
|
this.registerListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected renderBody(container: HTMLElement) {
|
||||||
|
this._container = container;
|
||||||
|
let viewBody = DOM.$('div.extension-view');
|
||||||
|
DOM.append(container, viewBody);
|
||||||
|
this._splitView = new SplitView(viewBody);
|
||||||
|
|
||||||
|
// Create a fixed list view for the account provider
|
||||||
|
let extensionTabViewContainer = DOM.$('.extensionTab-view');
|
||||||
|
let delegate = new ExtensionListDelegate(NewDashboardTabDialog.EXTENSIONLIST_HEIGHT);
|
||||||
|
let extensionTabRenderer = new ExtensionListRenderer();
|
||||||
|
this._extensionList = new List<IDashboardUITab>(extensionTabViewContainer, delegate, [extensionTabRenderer]);
|
||||||
|
this._extensionTabView = new FixedListView<IDashboardUITab>(
|
||||||
|
undefined,
|
||||||
|
false,
|
||||||
|
localize('allFeatures', 'All features'),
|
||||||
|
this._extensionList,
|
||||||
|
extensionTabViewContainer,
|
||||||
|
22,
|
||||||
|
[],
|
||||||
|
undefined,
|
||||||
|
this._contextMenuService,
|
||||||
|
this._keybindingService,
|
||||||
|
this._themeService
|
||||||
|
);
|
||||||
|
|
||||||
|
// Append the list view to the split view
|
||||||
|
this._splitView.addView(this._extensionTabView);
|
||||||
|
this._register(attachListStyler(this._extensionList, this._themeService));
|
||||||
|
|
||||||
|
let listService = <ListService>this._listService;
|
||||||
|
this._register(listService.register(this._extensionList));
|
||||||
|
this._splitView.layout(DOM.getContentHeight(this._container));
|
||||||
|
}
|
||||||
|
|
||||||
|
private registerListeners(): void {
|
||||||
|
// Theme styler
|
||||||
|
this._register(attachButtonStyler(this._cancelButton, this._themeService));
|
||||||
|
this._register(attachButtonStyler(this._addNewTabButton, this._themeService));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Overwrite escape key behavior */
|
||||||
|
protected onClose() {
|
||||||
|
this.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Overwrite enter key behavior */
|
||||||
|
protected onAccept() {
|
||||||
|
this.addNewTabs();
|
||||||
|
}
|
||||||
|
|
||||||
|
public close() {
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
private addNewTabs() {
|
||||||
|
if (this._addNewTabButton.enabled) {
|
||||||
|
let selectedTabs = this._extensionList.getSelectedElements();
|
||||||
|
this._onAddTabs.fire(selectedTabs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public cancel() {
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
public open(dashboardTabs: Array<IDashboardTab>, openedTabs: Array<IDashboardTab>) {
|
||||||
|
this.show();
|
||||||
|
this._viewModel.updateDashboardTabs(dashboardTabs, openedTabs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private onUpdateTabList(tabs: IDashboardUITab[]) {
|
||||||
|
this._extensionTabView.updateList(tabs);
|
||||||
|
this.layout();
|
||||||
|
if (this._extensionList.length > 0) {
|
||||||
|
this._extensionList.setSelection([0]);
|
||||||
|
this._addNewTabButton.enabled = true;
|
||||||
|
this._addNewTabButton.focus();
|
||||||
|
} else {
|
||||||
|
this._addNewTabButton.enabled = false;
|
||||||
|
this._cancelButton.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public dispose(): void {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
import { INewDashboardTabDialogService } from 'sql/parts/dashboard/newDashboardTabDialog/interface';
|
||||||
|
import { NewDashboardTabDialog } from 'sql/parts/dashboard/newDashboardTabDialog/newDashboardTabDialog';
|
||||||
|
import { IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
|
||||||
|
import { IAngularEventingService, AngularEventType } from 'sql/services/angularEventing/angularEventingService';
|
||||||
|
import { IDashboardUITab } from 'sql/parts/dashboard/newDashboardTabDialog/newDashboardTabViewModel';
|
||||||
|
|
||||||
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
|
||||||
|
export class NewDashboardTabDialogService implements INewDashboardTabDialogService {
|
||||||
|
_serviceBrand: any;
|
||||||
|
|
||||||
|
// MEMBER VARIABLES ////////////////////////////////////////////////////
|
||||||
|
private _addNewTabDialog: NewDashboardTabDialog;
|
||||||
|
private _uri: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@IAngularEventingService private _angularEventService: IAngularEventingService,
|
||||||
|
@IInstantiationService private _instantiationService: IInstantiationService
|
||||||
|
) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open account dialog
|
||||||
|
*/
|
||||||
|
public showDialog(dashboardTabs: Array<IDashboardTab>, openedTabs: Array<IDashboardTab>, uri: string): void {
|
||||||
|
this._uri = uri;
|
||||||
|
let self = this;
|
||||||
|
|
||||||
|
// Create a new dialog if one doesn't exist
|
||||||
|
if (!this._addNewTabDialog) {
|
||||||
|
this._addNewTabDialog = this._instantiationService.createInstance(NewDashboardTabDialog);
|
||||||
|
this._addNewTabDialog.onCancel(() => { self.handleOnCancel(); });
|
||||||
|
this._addNewTabDialog.onAddTabs((selectedTabs) => { self.handleOnAddTabs(selectedTabs); });
|
||||||
|
this._addNewTabDialog.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the dialog
|
||||||
|
this._addNewTabDialog.open(dashboardTabs, openedTabs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// PRIVATE HELPERS /////////////////////////////////////////////////////
|
||||||
|
private handleOnAddTabs(selectedUiTabs: Array<IDashboardUITab>): void {
|
||||||
|
let selectedTabs = selectedUiTabs.map(tab => tab.tabConfig);
|
||||||
|
this._angularEventService.sendAngularEvent(this._uri, AngularEventType.NEW_TABS, { dashboardTabs: selectedTabs });
|
||||||
|
this._addNewTabDialog.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleOnCancel(): void { }
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
import * as data from 'data';
|
||||||
|
import Event, { Emitter } from 'vs/base/common/event';
|
||||||
|
|
||||||
|
import { IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
|
||||||
|
|
||||||
|
|
||||||
|
export interface IDashboardUITab {
|
||||||
|
tabConfig: IDashboardTab;
|
||||||
|
isOpened?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View model for new dashboard tab
|
||||||
|
*/
|
||||||
|
export class NewDashboardTabViewModel {
|
||||||
|
|
||||||
|
// EVENTING ///////////////////////////////////////////////////////
|
||||||
|
private _updateTabListEmitter: Emitter<IDashboardUITab[]>;
|
||||||
|
public get updateTabListEvent(): Event<IDashboardUITab[]> { return this._updateTabListEmitter.event; }
|
||||||
|
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
// Create event emitters
|
||||||
|
this._updateTabListEmitter = new Emitter<IDashboardUITab[]>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateDashboardTabs(dashboardTabs: Array<IDashboardTab>, openedTabs: Array<IDashboardTab>) {
|
||||||
|
let tabList: IDashboardUITab[] = [];
|
||||||
|
dashboardTabs.forEach(tab => {
|
||||||
|
tabList.push({ tabConfig: tab });
|
||||||
|
});
|
||||||
|
openedTabs.forEach(tab => {
|
||||||
|
let uiTab = tabList.find(i => i.tabConfig === tab);
|
||||||
|
if (uiTab) {
|
||||||
|
uiTab.isOpened = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this._updateTabListEmitter.fire(tabList);
|
||||||
|
}
|
||||||
|
}
|
||||||
102
src/sql/parts/dashboard/pages/dashboardPageContribution.ts
Normal file
102
src/sql/parts/dashboard/pages/dashboardPageContribution.ts
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
import { Extensions, IDashboardWidgetRegistry } from 'sql/platform/dashboard/common/widgetRegistry';
|
||||||
|
|
||||||
|
import { Registry } from 'vs/platform/registry/common/platform';
|
||||||
|
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||||
|
import { mixin } from 'vs/base/common/objects';
|
||||||
|
import { localize } from 'vs/nls';
|
||||||
|
|
||||||
|
let widgetRegistry = <IDashboardWidgetRegistry>Registry.as(Extensions.DashboardWidgetContribution);
|
||||||
|
|
||||||
|
export function generateDashboardWidgetSchema(type?: 'database' | 'server', extension?: boolean): IJSONSchema {
|
||||||
|
let schemas;
|
||||||
|
if (extension) {
|
||||||
|
let extensionSchemas = type === 'server' ? widgetRegistry.serverWidgetSchema.extensionProperties : type === 'database' ? widgetRegistry.databaseWidgetSchema.extensionProperties : widgetRegistry.allSchema.extensionProperties;
|
||||||
|
schemas = type === 'server' ? widgetRegistry.serverWidgetSchema.properties : type === 'database' ? widgetRegistry.databaseWidgetSchema.properties : widgetRegistry.allSchema.properties;
|
||||||
|
schemas = mixin(schemas, extensionSchemas, true);
|
||||||
|
} else {
|
||||||
|
schemas = type === 'server' ? widgetRegistry.serverWidgetSchema.properties : type === 'database' ? widgetRegistry.databaseWidgetSchema.properties : widgetRegistry.allSchema.properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
name: {
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
provider: {
|
||||||
|
anyOf: [
|
||||||
|
{
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
edition: {
|
||||||
|
anyOf: [
|
||||||
|
{
|
||||||
|
type: 'number'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'number'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
gridItemConfig: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
sizex: {
|
||||||
|
type: 'number'
|
||||||
|
},
|
||||||
|
sizey: {
|
||||||
|
type: 'number'
|
||||||
|
},
|
||||||
|
col: {
|
||||||
|
type: 'number'
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
type: 'number'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
widget: {
|
||||||
|
type: 'object',
|
||||||
|
properties: schemas,
|
||||||
|
minItems: 1,
|
||||||
|
maxItems: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateDashboardTabSchema(type?: 'database' | 'server'): IJSONSchema {
|
||||||
|
return {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
tabId: {
|
||||||
|
type: 'string',
|
||||||
|
description: localize('sqlops.extension.contributes.dashboard.tab.id', "Unique identifier for this tab. Will be passed to the extension for any requests."),
|
||||||
|
enum: [],
|
||||||
|
enumDescriptions: [],
|
||||||
|
errorMessage: localize('dashboardTabError', "Extension tab is unknown or not installed.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DASHBOARD_CONFIG_ID = 'Dashboard';
|
||||||
|
export const DASHBOARD_TABS_KEY_PROPERTY = 'tabId';
|
||||||
@@ -10,6 +10,7 @@ import { BreadcrumbClass } from 'sql/parts/dashboard/services/breadcrumb.service
|
|||||||
import { IBreadcrumbService } from 'sql/base/browser/ui/breadcrumb/interfaces';
|
import { IBreadcrumbService } from 'sql/base/browser/ui/breadcrumb/interfaces';
|
||||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||||
import { WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
import { WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||||
|
import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService';
|
||||||
|
|
||||||
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
@@ -34,19 +35,21 @@ 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, el, _cd);
|
super(dashboardService, bootstrapService, el, _cd);
|
||||||
this._register(dashboardService.onUpdatePage(() => {
|
this._register(dashboardService.onUpdatePage(() => {
|
||||||
this.refresh(true);
|
this.refresh(true);
|
||||||
this._cd.detectChanges();
|
this._cd.detectChanges();
|
||||||
}));
|
}));
|
||||||
this.init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
this.init();
|
||||||
this._breadcrumbService.setBreadcrumbs(BreadcrumbClass.DatabasePage);
|
this._breadcrumbService.setBreadcrumbs(BreadcrumbClass.DatabasePage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,9 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { Registry } from 'vs/platform/registry/common/platform';
|
|
||||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||||
import { Extensions, IDashboardWidgetRegistry } from 'sql/platform/dashboard/common/widgetRegistry';
|
|
||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
|
import { generateDashboardWidgetSchema, generateDashboardTabSchema } from './dashboardPageContribution';
|
||||||
let widgetRegistry = <IDashboardWidgetRegistry>Registry.as(Extensions.DashboardWidgetContribution);
|
|
||||||
|
|
||||||
export const databaseDashboardPropertiesSchema: IJSONSchema = {
|
export const databaseDashboardPropertiesSchema: IJSONSchema = {
|
||||||
description: nls.localize('dashboardDatabaseProperties', 'Enable or disable the properties widget'),
|
description: nls.localize('dashboardDatabaseProperties', 'Enable or disable the properties widget'),
|
||||||
@@ -85,58 +82,7 @@ export const databaseDashboardPropertiesSchema: IJSONSchema = {
|
|||||||
export const databaseDashboardSettingSchema: IJSONSchema = {
|
export const databaseDashboardSettingSchema: IJSONSchema = {
|
||||||
type: ['array'],
|
type: ['array'],
|
||||||
description: nls.localize('dashboardDatabase', 'Customizes the database dashboard page'),
|
description: nls.localize('dashboardDatabase', 'Customizes the database dashboard page'),
|
||||||
items: <IJSONSchema>{
|
items: generateDashboardWidgetSchema('database'),
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
name: {
|
|
||||||
type: 'string'
|
|
||||||
},
|
|
||||||
icon: {
|
|
||||||
type: 'string'
|
|
||||||
},
|
|
||||||
provider: {
|
|
||||||
anyOf: [
|
|
||||||
'string',
|
|
||||||
{
|
|
||||||
type: 'array',
|
|
||||||
items: 'string'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
edition: {
|
|
||||||
anyOf: [
|
|
||||||
'number',
|
|
||||||
{
|
|
||||||
type: 'array',
|
|
||||||
items: 'number'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
gridItemConfig: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
sizex: {
|
|
||||||
type: 'number'
|
|
||||||
},
|
|
||||||
sizey: {
|
|
||||||
type: 'number'
|
|
||||||
},
|
|
||||||
col: {
|
|
||||||
type: 'number'
|
|
||||||
},
|
|
||||||
row: {
|
|
||||||
type: 'number'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
widget: {
|
|
||||||
type: 'object',
|
|
||||||
properties: widgetRegistry.databaseWidgetSchema.properties,
|
|
||||||
minItems: 1,
|
|
||||||
maxItems: 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
default: [
|
default: [
|
||||||
{
|
{
|
||||||
name: 'Tasks',
|
name: 'Tasks',
|
||||||
@@ -160,5 +106,14 @@ export const databaseDashboardSettingSchema: IJSONSchema = {
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const databaseDashboardTabsSchema: IJSONSchema = {
|
||||||
|
type: ['array'],
|
||||||
|
description: nls.localize('dashboardDatabaseTabs', 'Customizes the database dashboard tabs'),
|
||||||
|
items: generateDashboardTabSchema('database'),
|
||||||
|
default: [
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
export const DATABASE_DASHBOARD_SETTING = 'dashboard.database.widgets';
|
export const DATABASE_DASHBOARD_SETTING = 'dashboard.database.widgets';
|
||||||
export const DATABASE_DASHBOARD_PROPERTIES = 'dashboard.database.properties';
|
export const DATABASE_DASHBOARD_PROPERTIES = 'dashboard.database.properties';
|
||||||
|
export const DATABASE_DASHBOARD_TABS = 'dashboard.database.tabs';
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { BreadcrumbClass } from 'sql/parts/dashboard/services/breadcrumb.service
|
|||||||
import { IBreadcrumbService } from 'sql/base/browser/ui/breadcrumb/interfaces';
|
import { IBreadcrumbService } from 'sql/base/browser/ui/breadcrumb/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 { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||||
|
import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService';
|
||||||
|
|
||||||
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
@@ -31,24 +32,26 @@ export class ServerDashboardPage extends DashboardPage implements OnInit {
|
|||||||
};
|
};
|
||||||
|
|
||||||
protected readonly context = 'server';
|
protected readonly context = 'server';
|
||||||
|
private _letDashboardPromise: Thenable<boolean>;
|
||||||
|
|
||||||
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, el, cd);
|
super(dashboardService, bootstrapService, el, _cd);
|
||||||
// revert back to default database
|
// revert back to default database
|
||||||
this.dashboardService.connectionManagementService.changeDatabase('master').then(() => {
|
this._letDashboardPromise = this.dashboardService.connectionManagementService.changeDatabase('master');
|
||||||
this.dashboardService.connectionManagementService.connectionInfo.connectionProfile.databaseName = undefined;
|
|
||||||
this.init();
|
|
||||||
cd.detectChanges();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.breadcrumbService.setBreadcrumbs(BreadcrumbClass.ServerPage);
|
this._letDashboardPromise.then(() => {
|
||||||
this.dashboardService.connectionManagementService.connectionInfo.connectionProfile.databaseName = null;
|
this.breadcrumbService.setBreadcrumbs(BreadcrumbClass.ServerPage);
|
||||||
|
this.dashboardService.connectionManagementService.connectionInfo.connectionProfile.databaseName = null;
|
||||||
|
this.init();
|
||||||
|
this._cd.detectChanges();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,9 @@
|
|||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
import { Registry } from 'vs/platform/registry/common/platform';
|
|
||||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||||
import { Extensions, IDashboardWidgetRegistry } from 'sql/platform/dashboard/common/widgetRegistry';
|
|
||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
|
import { generateDashboardWidgetSchema, generateDashboardTabSchema } from 'sql/parts/dashboard/pages/dashboardPageContribution';
|
||||||
let widgetRegistry = <IDashboardWidgetRegistry>Registry.as(Extensions.DashboardWidgetContribution);
|
|
||||||
|
|
||||||
export interface IPropertiesConfig {
|
export interface IPropertiesConfig {
|
||||||
edition: number | Array<number>;
|
edition: number | Array<number>;
|
||||||
@@ -111,53 +108,18 @@ let defaultVal = [
|
|||||||
export const serverDashboardSettingSchema: IJSONSchema = {
|
export const serverDashboardSettingSchema: IJSONSchema = {
|
||||||
type: ['array'],
|
type: ['array'],
|
||||||
description: nls.localize('dashboardServer', 'Customizes the server dashboard page'),
|
description: nls.localize('dashboardServer', 'Customizes the server dashboard page'),
|
||||||
items: <IJSONSchema>{
|
items: generateDashboardWidgetSchema('server'),
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
name: {
|
|
||||||
type: 'string'
|
|
||||||
},
|
|
||||||
icon: {
|
|
||||||
type: 'string'
|
|
||||||
},
|
|
||||||
provider: {
|
|
||||||
anyOf: [
|
|
||||||
'string',
|
|
||||||
{
|
|
||||||
type: 'array',
|
|
||||||
items: 'string'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
edition: {
|
|
||||||
anyOf: [
|
|
||||||
'number',
|
|
||||||
{
|
|
||||||
type: 'array',
|
|
||||||
items: 'number'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
gridItemConfig: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
sizex: {
|
|
||||||
type: 'number'
|
|
||||||
},
|
|
||||||
sizey: {
|
|
||||||
type: 'number'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
widget: {
|
|
||||||
type: 'object',
|
|
||||||
properties: widgetRegistry.serverWidgetSchema.properties,
|
|
||||||
maxItems: 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
default: defaultVal
|
default: defaultVal
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const serverDashboardTabsSchema: IJSONSchema = {
|
||||||
|
type: ['array'],
|
||||||
|
description: nls.localize('dashboardServerTabs', 'Customizes the Server dashboard tabs'),
|
||||||
|
items: generateDashboardTabSchema('server'),
|
||||||
|
default: [
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
export const SERVER_DASHBOARD_SETTING = 'dashboard.server.widgets';
|
export const SERVER_DASHBOARD_SETTING = 'dashboard.server.widgets';
|
||||||
export const SERVER_DASHBOARD_PROPERTIES = 'dashboard.server.properties';
|
export const SERVER_DASHBOARD_PROPERTIES = 'dashboard.server.properties';
|
||||||
|
export const SERVER_DASHBOARD_TABS = 'dashboard.server.tabs';
|
||||||
@@ -21,6 +21,8 @@ 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 } from 'sql/services/angularEventing/angularEventingService';
|
||||||
|
import { IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
|
||||||
|
import { PinConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||||
|
|
||||||
import { ProviderMetadata, DatabaseInfo, SimpleExecuteResult } from 'data';
|
import { ProviderMetadata, DatabaseInfo, SimpleExecuteResult } from 'data';
|
||||||
|
|
||||||
@@ -30,15 +32,18 @@ import { IContextMenuService, IContextViewService } from 'vs/platform/contextvie
|
|||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||||
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||||
import { ConfigurationEditingService, IConfigurationValue } from 'vs/workbench/services/configuration/node/configurationEditingService'
|
import { ConfigurationEditingService, IConfigurationValue } from 'vs/workbench/services/configuration/node/configurationEditingService';
|
||||||
import { IMessageService } from 'vs/platform/message/common/message';
|
import { IMessageService } from 'vs/platform/message/common/message';
|
||||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||||
import Event, { Emitter } from 'vs/base/common/event';
|
import Event, { Emitter } from 'vs/base/common/event';
|
||||||
import Severity from 'vs/base/common/severity';
|
import Severity from 'vs/base/common/severity';
|
||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
|
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||||
import { deepClone } from 'vs/base/common/objects';
|
import { deepClone } from 'vs/base/common/objects';
|
||||||
|
|
||||||
|
import { IDashboardWebviewService } from 'sql/services/dashboardWebview/common/dashboardWebviewService';
|
||||||
|
|
||||||
const DASHBOARD_SETTINGS = 'dashboard';
|
const DASHBOARD_SETTINGS = 'dashboard';
|
||||||
|
|
||||||
/* Wrapper for a metadata service that contains the uri string to use on each request */
|
/* Wrapper for a metadata service that contains the uri string to use on each request */
|
||||||
@@ -128,6 +133,8 @@ export class DashboardServiceInterface implements OnDestroy {
|
|||||||
private _storageService: IStorageService;
|
private _storageService: IStorageService;
|
||||||
private _capabilitiesService: ICapabilitiesService;
|
private _capabilitiesService: ICapabilitiesService;
|
||||||
private _configurationEditingService: ConfigurationEditingService;
|
private _configurationEditingService: ConfigurationEditingService;
|
||||||
|
private _dashboardWebviewService: IDashboardWebviewService;
|
||||||
|
private _partService: IPartService;
|
||||||
|
|
||||||
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;
|
||||||
@@ -135,6 +142,15 @@ export class DashboardServiceInterface implements OnDestroy {
|
|||||||
private _onDeleteWidget = new Emitter<string>();
|
private _onDeleteWidget = new Emitter<string>();
|
||||||
public readonly onDeleteWidget: Event<string> = this._onDeleteWidget.event;
|
public readonly onDeleteWidget: Event<string> = this._onDeleteWidget.event;
|
||||||
|
|
||||||
|
private _onPinUnpinTab = new Emitter<PinConfig>();
|
||||||
|
public readonly onPinUnpinTab: Event<PinConfig> = this._onPinUnpinTab.event;
|
||||||
|
|
||||||
|
private _onAddNewTabs = new Emitter<Array<IDashboardTab>>();
|
||||||
|
public readonly onAddNewTabs: Event<Array<IDashboardTab>> = this._onAddNewTabs.event;
|
||||||
|
|
||||||
|
private _onCloseTab = new Emitter<string>();
|
||||||
|
public readonly onCloseTab: Event<string> = this._onCloseTab.event;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(BOOTSTRAP_SERVICE_ID) private _bootstrapService: IBootstrapService,
|
@Inject(BOOTSTRAP_SERVICE_ID) private _bootstrapService: IBootstrapService,
|
||||||
@Inject(forwardRef(() => Router)) private _router: Router,
|
@Inject(forwardRef(() => Router)) private _router: Router,
|
||||||
@@ -150,6 +166,8 @@ export class DashboardServiceInterface implements OnDestroy {
|
|||||||
this._storageService = this._bootstrapService.storageService;
|
this._storageService = this._bootstrapService.storageService;
|
||||||
this._capabilitiesService = this._bootstrapService.capabilitiesService;
|
this._capabilitiesService = this._bootstrapService.capabilitiesService;
|
||||||
this._configurationEditingService = this._bootstrapService.configurationEditorService;
|
this._configurationEditingService = this._bootstrapService.configurationEditorService;
|
||||||
|
this._dashboardWebviewService = this._bootstrapService.dashboardWebviewService;
|
||||||
|
this._partService = this._bootstrapService.partService;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
@@ -184,6 +202,14 @@ export class DashboardServiceInterface implements OnDestroy {
|
|||||||
return this._instantiationService;
|
return this._instantiationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get dashboardWebviewService(): IDashboardWebviewService {
|
||||||
|
return this._dashboardWebviewService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get partService(): IPartService {
|
||||||
|
return this._partService;
|
||||||
|
}
|
||||||
|
|
||||||
public get adminService(): SingleAdminService {
|
public get adminService(): SingleAdminService {
|
||||||
return this._adminService;
|
return this._adminService;
|
||||||
}
|
}
|
||||||
@@ -255,8 +281,8 @@ export class DashboardServiceInterface implements OnDestroy {
|
|||||||
return deepClone(config);
|
return deepClone(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public writeSettings(key: string, value: any, target: ConfigurationTarget) {
|
public writeSettings(type: string, value: any, target: ConfigurationTarget) {
|
||||||
this._configurationEditingService.writeConfiguration(target, { key: DASHBOARD_SETTINGS + '.' + key + '.widgets', value });
|
this._configurationEditingService.writeConfiguration(target, { key: [DASHBOARD_SETTINGS, type].join('.'), value });
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleDashboardEvent(event: IAngularEvent): void {
|
private handleDashboardEvent(event: IAngularEvent): void {
|
||||||
@@ -284,6 +310,15 @@ export class DashboardServiceInterface implements OnDestroy {
|
|||||||
break;
|
break;
|
||||||
case AngularEventType.DELETE_WIDGET:
|
case AngularEventType.DELETE_WIDGET:
|
||||||
this._onDeleteWidget.fire(event.payload.id);
|
this._onDeleteWidget.fire(event.payload.id);
|
||||||
|
break;
|
||||||
|
case AngularEventType.PINUNPIN_TAB:
|
||||||
|
this._onPinUnpinTab.fire(event.payload);
|
||||||
|
break;
|
||||||
|
case AngularEventType.NEW_TABS:
|
||||||
|
this._onAddNewTabs.fire(event.payload.dashboardTabs);
|
||||||
|
break;
|
||||||
|
case AngularEventType.CLOSE_TAB:
|
||||||
|
this._onCloseTab.fire(event.payload.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
126
src/sql/parts/dashboard/tabs/dashboardWebviewTab.component.ts
Normal file
126
src/sql/parts/dashboard/tabs/dashboardWebviewTab.component.ts
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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!./dashboardWebviewTab';
|
||||||
|
|
||||||
|
import { Component, forwardRef, Input, OnInit, Inject, ChangeDetectorRef, ElementRef } from '@angular/core';
|
||||||
|
|
||||||
|
import Event, { Emitter } from 'vs/base/common/event';
|
||||||
|
import Webview from 'vs/workbench/parts/html/browser/webview';
|
||||||
|
import { Parts } from 'vs/workbench/services/part/common/partService';
|
||||||
|
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||||
|
|
||||||
|
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
|
||||||
|
import { TabConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||||
|
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||||
|
import { IDashboardWebview } from 'sql/services/dashboardWebview/common/dashboardWebviewService';
|
||||||
|
|
||||||
|
import * as data from 'data';
|
||||||
|
import { memoize } from 'vs/base/common/decorators';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: '',
|
||||||
|
selector: 'dashboard-webview-tab',
|
||||||
|
providers: [{ provide: DashboardTab, useExisting: forwardRef(() => DashboardWebviewTab) }]
|
||||||
|
})
|
||||||
|
export class DashboardWebviewTab extends DashboardTab implements OnInit, IDashboardWebview {
|
||||||
|
@Input() private tab: TabConfig;
|
||||||
|
|
||||||
|
private _onResize = new Emitter<void>();
|
||||||
|
public readonly onResize: Event<void> = this._onResize.event;
|
||||||
|
private _onMessage = new Emitter<string>();
|
||||||
|
public readonly onMessage: Event<string> = this._onMessage.event;
|
||||||
|
private _onMessageDisposable: IDisposable;
|
||||||
|
private _webview: Webview;
|
||||||
|
private _html: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@Inject(forwardRef(() => DashboardServiceInterface)) private _dashboardService: DashboardServiceInterface,
|
||||||
|
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
|
||||||
|
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this._dashboardService.dashboardWebviewService.registerWebview(this);
|
||||||
|
this._createWebview();
|
||||||
|
}
|
||||||
|
|
||||||
|
public layout(): void {
|
||||||
|
this._createWebview();
|
||||||
|
}
|
||||||
|
|
||||||
|
public get id(): string {
|
||||||
|
return this.tab.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get editable(): boolean {
|
||||||
|
return this.tab.editable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@memoize
|
||||||
|
public get connection(): data.connection.Connection {
|
||||||
|
let currentConnection = this._dashboardService.connectionManagementService.connectionInfo.connectionProfile;
|
||||||
|
let connection: data.connection.Connection = {
|
||||||
|
providerName: currentConnection.providerName,
|
||||||
|
connectionId: currentConnection.id,
|
||||||
|
options: currentConnection.options
|
||||||
|
};
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@memoize
|
||||||
|
public get serverInfo(): data.ServerInfo {
|
||||||
|
return this._dashboardService.connectionManagementService.connectionInfo.serverInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public refresh(): void {
|
||||||
|
// no op
|
||||||
|
}
|
||||||
|
|
||||||
|
public setHtml(html: string): void {
|
||||||
|
this._html = html;
|
||||||
|
if (this._webview) {
|
||||||
|
this._webview.contents = [html];
|
||||||
|
this._webview.layout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sendMessage(message: string): void {
|
||||||
|
if (this._webview) {
|
||||||
|
this._webview.sendMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _createWebview(): void {
|
||||||
|
if (this._webview) {
|
||||||
|
this._webview.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._onMessageDisposable) {
|
||||||
|
this._onMessageDisposable.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._webview = new Webview(this._el.nativeElement,
|
||||||
|
this._dashboardService.partService.getContainer(Parts.EDITOR_PART),
|
||||||
|
this._dashboardService.contextViewService,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
allowScripts: true,
|
||||||
|
enableWrappedPostMessage: true,
|
||||||
|
hideFind: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this._onMessageDisposable = this._webview.onMessage(e => {
|
||||||
|
this._onMessage.fire(e);
|
||||||
|
});
|
||||||
|
this._webview.style(this._dashboardService.themeService.getTheme());
|
||||||
|
if (this._html) {
|
||||||
|
this._webview.contents = [this._html];
|
||||||
|
}
|
||||||
|
this._webview.layout();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||||
|
import * as nls from 'vs/nls';
|
||||||
|
|
||||||
|
import { registerTabContent } from 'sql/platform/dashboard/common/dashboardRegistry';
|
||||||
|
|
||||||
|
export const WEBVIEW_TABS = 'webview-tab';
|
||||||
|
|
||||||
|
let webviewSchema: IJSONSchema = {
|
||||||
|
type: 'null',
|
||||||
|
description: nls.localize('dashboard.tab.widgets', "The list of widgets that will be displayed in this tab."),
|
||||||
|
default: null
|
||||||
|
};
|
||||||
|
|
||||||
|
registerTabContent(WEBVIEW_TABS, webviewSchema);
|
||||||
10
src/sql/parts/dashboard/tabs/dashboardWebviewTab.css
Normal file
10
src/sql/parts/dashboard/tabs/dashboardWebviewTab.css
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
dashboard-webview-tab {
|
||||||
|
height: 100%;
|
||||||
|
width : 100%;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<!--
|
||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
-->
|
||||||
|
<div [ngGrid]="gridConfig" *ngIf="widgets" >
|
||||||
|
<dashboard-widget-wrapper *ngFor="let widget of widgets" [(ngGridItem)]="widget.gridItemConfig" [_config]="widget">
|
||||||
|
</dashboard-widget-wrapper>
|
||||||
|
</div>
|
||||||
228
src/sql/parts/dashboard/tabs/dashboardWidgetTab.component.ts
Normal file
228
src/sql/parts/dashboard/tabs/dashboardWidgetTab.component.ts
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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!./dashboardWidgetTab';
|
||||||
|
|
||||||
|
import { Component, Inject, Input, forwardRef, ViewChild, ElementRef, ViewChildren, QueryList, OnDestroy, ChangeDetectorRef, EventEmitter, OnChanges } from '@angular/core';
|
||||||
|
import { NgGridConfig, NgGrid, NgGridItem } from 'angular2-grid';
|
||||||
|
|
||||||
|
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||||
|
import { TabConfig, WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||||
|
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/common/dashboardWidgetWrapper.component';
|
||||||
|
import { subscriptionToDisposable } from 'sql/base/common/lifecycle';
|
||||||
|
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
|
||||||
|
|
||||||
|
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||||
|
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||||
|
import * as objects from 'vs/base/common/objects';
|
||||||
|
import Event, { Emitter } from 'vs/base/common/event';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorting function for dashboard widgets
|
||||||
|
* In order of priority;
|
||||||
|
* If neither have defined grid positions, they are equivalent
|
||||||
|
* If a has a defined grid position and b does not; a should come first
|
||||||
|
* If both have defined grid positions and have the same row; the one with the smaller col position should come first
|
||||||
|
* If both have defined grid positions but different rows (it doesn't really matter in this case) the lowers row should come first
|
||||||
|
*/
|
||||||
|
function configSorter(a, b): number {
|
||||||
|
if ((!a.gridItemConfig || !a.gridItemConfig.col)
|
||||||
|
&& (!b.gridItemConfig || !b.gridItemConfig.col)) {
|
||||||
|
return 0;
|
||||||
|
} else if (!a.gridItemConfig || !a.gridItemConfig.col) {
|
||||||
|
return 1;
|
||||||
|
} else if (!b.gridItemConfig || !b.gridItemConfig.col) {
|
||||||
|
return -1;
|
||||||
|
} else if (a.gridItemConfig.row === b.gridItemConfig.row) {
|
||||||
|
if (a.gridItemConfig.col < b.gridItemConfig.col) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.gridItemConfig.col === b.gridItemConfig.col) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.gridItemConfig.col > b.gridItemConfig.col) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (a.gridItemConfig.row < b.gridItemConfig.row) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.gridItemConfig.row === b.gridItemConfig.row) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.gridItemConfig.row > b.gridItemConfig.row) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return void 0; // this should never be reached
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'dashboard-widget-tab',
|
||||||
|
templateUrl: decodeURI(require.toUrl('sql/parts/dashboard/tabs/dashboardWidgetTab.component.html')),
|
||||||
|
providers: [{ provide: DashboardTab, useExisting: forwardRef(() => DashboardWidgetTab) }]
|
||||||
|
})
|
||||||
|
export class DashboardWidgetTab extends DashboardTab implements OnDestroy, OnChanges {
|
||||||
|
@Input() private tab: TabConfig;
|
||||||
|
private widgets: WidgetConfig[];
|
||||||
|
private _onResize = new Emitter<void>();
|
||||||
|
public readonly onResize: Event<void> = this._onResize.event;
|
||||||
|
|
||||||
|
protected SKELETON_WIDTH = 5;
|
||||||
|
protected gridConfig: NgGridConfig = {
|
||||||
|
'margins': [10], // The size of the margins of each item. Supports up to four values in the same way as CSS margins. Can be updated using setMargins()
|
||||||
|
'draggable': false, // Whether the items can be dragged. Can be updated using enableDrag()/disableDrag()
|
||||||
|
'resizable': false, // Whether the items can be resized. Can be updated using enableResize()/disableResize()
|
||||||
|
'max_cols': this.SKELETON_WIDTH, // The maximum number of columns allowed. Set to 0 for infinite. Cannot be used with max_rows
|
||||||
|
'max_rows': 0, // The maximum number of rows allowed. Set to 0 for infinite. Cannot be used with max_cols
|
||||||
|
'visible_cols': 0, // The number of columns shown on screen when auto_resize is set to true. Set to 0 to not auto_resize. Will be overriden by max_cols
|
||||||
|
'visible_rows': 0, // The number of rows shown on screen when auto_resize is set to true. Set to 0 to not auto_resize. Will be overriden by max_rows
|
||||||
|
'min_cols': 0, // The minimum number of columns allowed. Can be any number greater than or equal to 1.
|
||||||
|
'min_rows': 0, // The minimum number of rows allowed. Can be any number greater than or equal to 1.
|
||||||
|
'col_width': 250, // The width of each column
|
||||||
|
'row_height': 250, // The height of each row
|
||||||
|
'cascade': 'left', // The direction to cascade grid items ('up', 'right', 'down', 'left')
|
||||||
|
'min_width': 100, // The minimum width of an item. If greater than col_width, this will update the value of min_cols
|
||||||
|
'min_height': 100, // The minimum height of an item. If greater than row_height, this will update the value of min_rows
|
||||||
|
'fix_to_grid': false, // Fix all item movements to the grid
|
||||||
|
'auto_style': true, // Automatically add required element styles at run-time
|
||||||
|
'auto_resize': false, // Automatically set col_width/row_height so that max_cols/max_rows fills the screen. Only has effect is max_cols or max_rows is set
|
||||||
|
'maintain_ratio': false, // Attempts to maintain aspect ratio based on the colWidth/rowHeight values set in the config
|
||||||
|
'prefer_new': false, // When adding new items, will use that items position ahead of existing items
|
||||||
|
'limit_to_screen': true, // When resizing the screen, with this true and auto_resize false, the grid will re-arrange to fit the screen size. Please note, at present this only works with cascade direction up.
|
||||||
|
};
|
||||||
|
|
||||||
|
private _editDispose: Array<IDisposable> = [];
|
||||||
|
|
||||||
|
@ViewChild(NgGrid) private _grid: NgGrid;
|
||||||
|
@ViewChildren(DashboardWidgetWrapper) private _widgets: QueryList<DashboardWidgetWrapper>;
|
||||||
|
@ViewChildren(NgGridItem) private _items: QueryList<NgGridItem>;
|
||||||
|
constructor(
|
||||||
|
@Inject(forwardRef(() => DashboardServiceInterface)) protected dashboardService: DashboardServiceInterface,
|
||||||
|
@Inject(forwardRef(() => ElementRef)) protected _el: ElementRef,
|
||||||
|
@Inject(forwardRef(() => ChangeDetectorRef)) protected _cd: ChangeDetectorRef
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected init() {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges() {
|
||||||
|
if (this.tab.content) {
|
||||||
|
this.widgets = Object.values(this.tab.content)[0];
|
||||||
|
this._cd.detectChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public get id(): string {
|
||||||
|
return this.tab.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get editable(): boolean {
|
||||||
|
return this.tab.editable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public layout() {
|
||||||
|
if (this._widgets) {
|
||||||
|
this._widgets.forEach(item => {
|
||||||
|
item.layout();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this._grid.triggerResize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public refresh(): void {
|
||||||
|
if (this._widgets) {
|
||||||
|
this._widgets.forEach(item => {
|
||||||
|
item.refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enableEdit(): void {
|
||||||
|
if (this._grid.dragEnable) {
|
||||||
|
this._grid.disableDrag();
|
||||||
|
this._grid.disableResize();
|
||||||
|
this._editDispose.forEach(i => i.dispose());
|
||||||
|
this._widgets.forEach(i => {
|
||||||
|
if (i.id) {
|
||||||
|
i.disableEdit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this._editDispose = [];
|
||||||
|
} else {
|
||||||
|
this._grid.enableResize();
|
||||||
|
this._grid.enableDrag();
|
||||||
|
this._editDispose.push(this.dashboardService.onDeleteWidget(e => {
|
||||||
|
let index = this.widgets.findIndex(i => i.id === e);
|
||||||
|
this.widgets.splice(index, 1);
|
||||||
|
|
||||||
|
index = this.tab.originalConfig.findIndex(i => i.id === e);
|
||||||
|
this.tab.originalConfig.splice(index, 1);
|
||||||
|
|
||||||
|
this._rewriteConfig();
|
||||||
|
this._cd.detectChanges();
|
||||||
|
}));
|
||||||
|
this._editDispose.push(subscriptionToDisposable(this._grid.onResizeStop.subscribe((e: NgGridItem) => {
|
||||||
|
this._onResize.fire();
|
||||||
|
let event = e.getEventOutput();
|
||||||
|
let config = this.tab.originalConfig.find(i => i.id === event.payload.id);
|
||||||
|
|
||||||
|
if (!config.gridItemConfig) {
|
||||||
|
config.gridItemConfig = {};
|
||||||
|
}
|
||||||
|
config.gridItemConfig.sizex = e.sizex;
|
||||||
|
config.gridItemConfig.sizey = e.sizey;
|
||||||
|
|
||||||
|
let component = this._widgets.find(i => i.id === event.payload.id);
|
||||||
|
|
||||||
|
component.layout();
|
||||||
|
this._rewriteConfig();
|
||||||
|
})));
|
||||||
|
this._editDispose.push(subscriptionToDisposable(this._grid.onDragStop.subscribe((e: NgGridItem) => {
|
||||||
|
this._onResize.fire();
|
||||||
|
let event = e.getEventOutput();
|
||||||
|
this._items.forEach(i => {
|
||||||
|
let config = this.tab.originalConfig.find(j => j.id === i.getEventOutput().payload.id);
|
||||||
|
if ((config.gridItemConfig && config.gridItemConfig.col) || config.id === event.payload.id) {
|
||||||
|
if (!config.gridItemConfig) {
|
||||||
|
config.gridItemConfig = {};
|
||||||
|
}
|
||||||
|
config.gridItemConfig.col = i.col;
|
||||||
|
config.gridItemConfig.row = i.row;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.tab.originalConfig.sort(configSorter);
|
||||||
|
|
||||||
|
this._rewriteConfig();
|
||||||
|
})));
|
||||||
|
this._widgets.forEach(i => {
|
||||||
|
if (i.id) {
|
||||||
|
i.enableEdit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _rewriteConfig(): void {
|
||||||
|
let writeableConfig = objects.deepClone(this.tab.originalConfig);
|
||||||
|
|
||||||
|
writeableConfig.forEach(i => {
|
||||||
|
delete i.id;
|
||||||
|
});
|
||||||
|
let target: ConfigurationTarget = ConfigurationTarget.USER;
|
||||||
|
this.dashboardService.writeSettings([this.tab.context, 'widgets'].join('.'), writeableConfig, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||||
|
import * as nls from 'vs/nls';
|
||||||
|
|
||||||
|
import { generateDashboardWidgetSchema } from 'sql/parts/dashboard/pages/dashboardPageContribution';
|
||||||
|
import { registerTabContent } from 'sql/platform/dashboard/common/dashboardRegistry';
|
||||||
|
|
||||||
|
export const WIDGETS_TABS = 'widgets-tab';
|
||||||
|
|
||||||
|
let widgetsSchema: IJSONSchema = {
|
||||||
|
type: 'array',
|
||||||
|
description: nls.localize('dashboard.tab.content.widgets', "The list of widgets that will be displayed in this tab."),
|
||||||
|
items: generateDashboardWidgetSchema(undefined, true)
|
||||||
|
};
|
||||||
|
|
||||||
|
registerTabContent(WIDGETS_TABS, widgetsSchema);
|
||||||
9
src/sql/parts/dashboard/tabs/dashboardWidgetTab.css
Normal file
9
src/sql/parts/dashboard/tabs/dashboardWidgetTab.css
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
dashboard-tab {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
@@ -21,7 +21,7 @@ interface IInsightTypeContrib {
|
|||||||
|
|
||||||
registerDashboardWidget('insights-widget', '', insightsSchema);
|
registerDashboardWidget('insights-widget', '', insightsSchema);
|
||||||
|
|
||||||
ExtensionsRegistry.registerExtensionPoint<IInsightTypeContrib | IInsightTypeContrib[]>('insights', [], insightsContribution).setHandler(extensions => {
|
ExtensionsRegistry.registerExtensionPoint<IInsightTypeContrib | IInsightTypeContrib[]>('dashboard.insights', [], insightsContribution).setHandler(extensions => {
|
||||||
|
|
||||||
function handleCommand(insight: IInsightTypeContrib, extension: IExtensionPointUser<any>) {
|
function handleCommand(insight: IInsightTypeContrib, extension: IExtensionPointUser<any>) {
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,119 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { Component, Inject, forwardRef, ChangeDetectorRef, OnInit, ViewChild, ElementRef } from '@angular/core';
|
||||||
|
|
||||||
|
import Webview from 'vs/workbench/parts/html/browser/webview';
|
||||||
|
import { Parts } from 'vs/workbench/services/part/common/partService';
|
||||||
|
import Event, { Emitter } from 'vs/base/common/event';
|
||||||
|
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||||
|
import { memoize } from 'vs/base/common/decorators';
|
||||||
|
|
||||||
|
import { DashboardWidget, IDashboardWidget, WidgetConfig, WIDGET_CONFIG } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||||
|
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||||
|
import { IDashboardWebview } from 'sql/services/dashboardWebview/common/dashboardWebviewService';
|
||||||
|
|
||||||
|
import * as data from 'data';
|
||||||
|
|
||||||
|
interface IWebviewWidgetConfig {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selector = 'webview-widget';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: selector,
|
||||||
|
template: '<div></div>'
|
||||||
|
})
|
||||||
|
export class WebviewWidget extends DashboardWidget implements IDashboardWidget, OnInit, IDashboardWebview {
|
||||||
|
|
||||||
|
private _id: string;
|
||||||
|
private _webview: Webview;
|
||||||
|
private _html: string;
|
||||||
|
private _onMessage = new Emitter<string>();
|
||||||
|
public readonly onMessage: Event<string> = this._onMessage.event;
|
||||||
|
private _onMessageDisposable: IDisposable;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@Inject(forwardRef(() => DashboardServiceInterface)) private _dashboardService: DashboardServiceInterface,
|
||||||
|
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
|
||||||
|
@Inject(WIDGET_CONFIG) protected _config: WidgetConfig,
|
||||||
|
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
this._id = (_config.widget[selector] as IWebviewWidgetConfig).id;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this._dashboardService.dashboardWebviewService.registerWebview(this);
|
||||||
|
this._createWebview();
|
||||||
|
}
|
||||||
|
|
||||||
|
public get id(): string {
|
||||||
|
return this._id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setHtml(html: string): void {
|
||||||
|
this._html = html;
|
||||||
|
if (this._webview) {
|
||||||
|
this._webview.contents = [html];
|
||||||
|
this._webview.layout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@memoize
|
||||||
|
public get connection(): data.connection.Connection {
|
||||||
|
let currentConnection = this._dashboardService.connectionManagementService.connectionInfo.connectionProfile;
|
||||||
|
let connection: data.connection.Connection = {
|
||||||
|
providerName: currentConnection.providerName,
|
||||||
|
connectionId: currentConnection.id,
|
||||||
|
options: currentConnection.options
|
||||||
|
};
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@memoize
|
||||||
|
public get serverInfo(): data.ServerInfo {
|
||||||
|
return this._dashboardService.connectionManagementService.connectionInfo.serverInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public layout(): void {
|
||||||
|
this._createWebview();
|
||||||
|
}
|
||||||
|
|
||||||
|
public sendMessage(message: string): void {
|
||||||
|
if (this._webview) {
|
||||||
|
this._webview.sendMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _createWebview(): void {
|
||||||
|
if (this._webview) {
|
||||||
|
this._webview.dispose();
|
||||||
|
}
|
||||||
|
if (this._onMessageDisposable) {
|
||||||
|
this._onMessageDisposable.dispose();
|
||||||
|
}
|
||||||
|
this._webview = new Webview(this._el.nativeElement,
|
||||||
|
this._dashboardService.partService.getContainer(Parts.EDITOR_PART),
|
||||||
|
this._dashboardService.contextViewService,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
allowScripts: true,
|
||||||
|
enableWrappedPostMessage: true,
|
||||||
|
hideFind: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this._onMessageDisposable = this._webview.onMessage(e => {
|
||||||
|
this._onMessage.fire(e);
|
||||||
|
});
|
||||||
|
this._webview.style(this._dashboardService.themeService.getTheme());
|
||||||
|
if (this._html) {
|
||||||
|
this._webview.contents = [this._html];
|
||||||
|
}
|
||||||
|
this._webview.layout();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||||
|
import { registerDashboardWidget } from 'sql/platform/dashboard/common/widgetRegistry';
|
||||||
|
|
||||||
|
let webviewSchema: IJSONSchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
registerDashboardWidget('webview-widget', '', webviewSchema, undefined, { extensionOnly: true });
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
.fullsize {
|
.fullsize {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.headersVisible > .fullsize {
|
.headersVisible > .fullsize {
|
||||||
|
|||||||
@@ -4,23 +4,45 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { Registry } from 'vs/platform/registry/common/platform';
|
import { Registry } from 'vs/platform/registry/common/platform';
|
||||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
import { IConfigurationRegistry, Extensions as ConfigurationExtension } from 'vs/platform/configuration/common/configurationRegistry';
|
||||||
|
import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
|
||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
|
import { deepClone } from 'vs/base/common/objects';
|
||||||
import { IExtensionPointUser, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry';
|
import { IExtensionPointUser, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry';
|
||||||
|
|
||||||
import { ProviderProperties } from 'sql/parts/dashboard/widgets/properties/propertiesWidget.component';
|
import { ProviderProperties } from 'sql/parts/dashboard/widgets/properties/propertiesWidget.component';
|
||||||
|
import { DATABASE_DASHBOARD_TABS } from 'sql/parts/dashboard/pages/databaseDashboardPage.contribution';
|
||||||
|
import { SERVER_DASHBOARD_TABS, SERVER_DASHBOARD_PROPERTIES } from 'sql/parts/dashboard/pages/serverDashboardPage.contribution';
|
||||||
|
import { DASHBOARD_CONFIG_ID, DASHBOARD_TABS_KEY_PROPERTY } from 'sql/parts/dashboard/pages/dashboardPageContribution';
|
||||||
|
|
||||||
export const Extensions = {
|
export const Extensions = {
|
||||||
DashboardContributions: 'dashboard.contributions'
|
DashboardContributions: 'dashboard.contributions'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface IDashboardTab {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
publisher: string;
|
||||||
|
description?: string;
|
||||||
|
content?: object;
|
||||||
|
provider?: string | string[];
|
||||||
|
edition?: number | number[];
|
||||||
|
alwaysShow?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IDashboardRegistry {
|
export interface IDashboardRegistry {
|
||||||
registerDashboardProvider(id: string, properties: ProviderProperties): void;
|
registerDashboardProvider(id: string, properties: ProviderProperties): void;
|
||||||
getProperties(id: string): ProviderProperties;
|
getProperties(id: string): ProviderProperties;
|
||||||
|
registerTab(tab: IDashboardTab): void;
|
||||||
|
tabs: Array<IDashboardTab>;
|
||||||
|
tabContentSchemaProperties: IJSONSchemaMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
class DashboardRegistry implements IDashboardRegistry {
|
class DashboardRegistry implements IDashboardRegistry {
|
||||||
private _properties = new Map<string, ProviderProperties>();
|
private _properties = new Map<string, ProviderProperties>();
|
||||||
|
private _tabs = new Array<IDashboardTab>();
|
||||||
|
private _configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtension.Configuration);
|
||||||
|
private _dashboardTabContentSchemaProperties: IJSONSchemaMap = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a dashboard widget
|
* Register a dashboard widget
|
||||||
@@ -33,11 +55,57 @@ class DashboardRegistry implements IDashboardRegistry {
|
|||||||
public getProperties(id: string): ProviderProperties {
|
public getProperties(id: string): ProviderProperties {
|
||||||
return this._properties.get(id);
|
return this._properties.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public registerTab(tab: IDashboardTab): void {
|
||||||
|
this._tabs.push(tab);
|
||||||
|
let dashboardConfig = this._configurationRegistry.getConfigurations().find(c => c.id === DASHBOARD_CONFIG_ID);
|
||||||
|
|
||||||
|
if (dashboardConfig) {
|
||||||
|
let dashboardDatabaseTabProperty = (<IJSONSchema>dashboardConfig.properties[DATABASE_DASHBOARD_TABS].items).properties[DASHBOARD_TABS_KEY_PROPERTY];
|
||||||
|
dashboardDatabaseTabProperty.enum.push(tab.id);
|
||||||
|
dashboardDatabaseTabProperty.enumDescriptions.push(tab.description || '');
|
||||||
|
|
||||||
|
let dashboardServerTabProperty = (<IJSONSchema>dashboardConfig.properties[SERVER_DASHBOARD_TABS].items).properties[DASHBOARD_TABS_KEY_PROPERTY];
|
||||||
|
dashboardServerTabProperty.enum.push(tab.id);
|
||||||
|
dashboardServerTabProperty.enumDescriptions.push(tab.description || '');
|
||||||
|
|
||||||
|
this._configurationRegistry.notifyConfigurationSchemaUpdated(dashboardConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get tabs(): Array<IDashboardTab> {
|
||||||
|
return this._tabs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a dashboard widget
|
||||||
|
* @param id id of the widget
|
||||||
|
* @param schema config schema of the widget
|
||||||
|
*/
|
||||||
|
public registerTabContent(id: string, schema: IJSONSchema): void {
|
||||||
|
this._dashboardTabContentSchemaProperties[id] = schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get tabContentSchemaProperties(): IJSONSchemaMap {
|
||||||
|
return deepClone(this._dashboardTabContentSchemaProperties);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const dashboardRegistry = new DashboardRegistry();
|
const dashboardRegistry = new DashboardRegistry();
|
||||||
Registry.add(Extensions.DashboardContributions, dashboardRegistry);
|
Registry.add(Extensions.DashboardContributions, dashboardRegistry);
|
||||||
|
|
||||||
|
export function registerTab(tab: IDashboardTab): void {
|
||||||
|
dashboardRegistry.registerTab(tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerTabContent(id: string, schema: IJSONSchema): void {
|
||||||
|
dashboardRegistry.registerTabContent(id, schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateTabContentSchemaProperties(): IJSONSchemaMap {
|
||||||
|
return dashboardRegistry.tabContentSchemaProperties;
|
||||||
|
}
|
||||||
|
|
||||||
const dashboardPropertiesPropertyContrib: IJSONSchema = {
|
const dashboardPropertiesPropertyContrib: IJSONSchema = {
|
||||||
description: nls.localize('dashboard.properties.property', "Defines a property to show on the dashboard"),
|
description: nls.localize('dashboard.properties.property', "Defines a property to show on the dashboard"),
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
|||||||
@@ -5,8 +5,9 @@
|
|||||||
import { IInsightsConfig } from 'sql/parts/dashboard/widgets/insights/interfaces';
|
import { IInsightsConfig } from 'sql/parts/dashboard/widgets/insights/interfaces';
|
||||||
|
|
||||||
import * as platform from 'vs/platform/registry/common/platform';
|
import * as platform from 'vs/platform/registry/common/platform';
|
||||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
|
||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
|
import { deepClone } from 'vs/base/common/objects';
|
||||||
|
|
||||||
export type WidgetIdentifier = string;
|
export type WidgetIdentifier = string;
|
||||||
|
|
||||||
@@ -14,16 +15,27 @@ export const Extensions = {
|
|||||||
DashboardWidgetContribution: 'dashboard.contributions.widgets'
|
DashboardWidgetContribution: 'dashboard.contributions.widgets'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface IDashboardRegistryOptions {
|
||||||
|
extensionOnly: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CustomIJSONSchema extends IJSONSchema {
|
||||||
|
extensionProperties: IJSONSchemaMap;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IDashboardWidgetRegistry {
|
export interface IDashboardWidgetRegistry {
|
||||||
databaseWidgetSchema: IJSONSchema;
|
databaseWidgetSchema: CustomIJSONSchema;
|
||||||
serverWidgetSchema: IJSONSchema;
|
serverWidgetSchema: CustomIJSONSchema;
|
||||||
|
allSchema: CustomIJSONSchema;
|
||||||
registerWidget(id: string, description: string, schema: IJSONSchema, context?: 'database' | 'server'): WidgetIdentifier;
|
registerWidget(id: string, description: string, schema: IJSONSchema, context?: 'database' | 'server'): WidgetIdentifier;
|
||||||
registerNonCustomDashboardWidget(id: string, description: string, val: IInsightsConfig, context?: 'database' | 'server'): WidgetIdentifier;
|
registerNonCustomDashboardWidget(id: string, description: string, val: IInsightsConfig, context?: 'database' | 'server', options?: IDashboardRegistryOptions): WidgetIdentifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
class DashboardWidgetRegistry implements IDashboardWidgetRegistry {
|
class DashboardWidgetRegistry implements IDashboardWidgetRegistry {
|
||||||
private _dashboardWidgetSchema: IJSONSchema = { type: 'object', description: nls.localize('schema.dashboardWidgets', 'Widget used in the dashboards'), properties: {}, additionalProperties: false };
|
private _allSchema: CustomIJSONSchema = { type: 'object', description: nls.localize('schema.dashboardWidgets', 'Widget used in the dashboards'), properties: {}, extensionProperties: {}, additionalProperties: false };
|
||||||
private _serverWidgetSchema: IJSONSchema = { type: 'object', description: nls.localize('schema.dashboardWidgets', 'Widget used in the dashboards'), properties: {}, additionalProperties: false };
|
private _dashboardWidgetSchema: CustomIJSONSchema = { type: 'object', description: nls.localize('schema.dashboardWidgets', 'Widget used in the dashboards'), properties: {}, extensionProperties: {}, additionalProperties: false };
|
||||||
|
private _serverWidgetSchema: CustomIJSONSchema = { type: 'object', description: nls.localize('schema.dashboardWidgets', 'Widget used in the dashboards'), properties: {}, extensionProperties: {}, additionalProperties: false };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a dashboard widget
|
* Register a dashboard widget
|
||||||
* @param id id of the widget
|
* @param id id of the widget
|
||||||
@@ -31,13 +43,27 @@ class DashboardWidgetRegistry implements IDashboardWidgetRegistry {
|
|||||||
* @param schema config schema of the widget
|
* @param schema config schema of the widget
|
||||||
* @param context either 'database' or 'server' for what page to register for; if not specified, will register for both
|
* @param context either 'database' or 'server' for what page to register for; if not specified, will register for both
|
||||||
*/
|
*/
|
||||||
public registerWidget(id: string, description: string, schema: IJSONSchema, context?: 'database' | 'server'): WidgetIdentifier {
|
public registerWidget(id: string, description: string, schema: IJSONSchema, context?: 'database' | 'server', options?: IDashboardRegistryOptions): WidgetIdentifier {
|
||||||
if (context === undefined || context === 'database') {
|
if (options && options.extensionOnly) {
|
||||||
this._dashboardWidgetSchema.properties[id] = schema;
|
if (context === undefined || context === 'database') {
|
||||||
}
|
this._dashboardWidgetSchema.extensionProperties[id] = schema;
|
||||||
|
}
|
||||||
|
|
||||||
if (context === undefined || context === 'server') {
|
if (context === undefined || context === 'server') {
|
||||||
this._serverWidgetSchema.properties[id] = schema;
|
this._serverWidgetSchema.extensionProperties[id] = schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._allSchema.extensionProperties[id] = schema;
|
||||||
|
} else {
|
||||||
|
if (context === undefined || context === 'database') {
|
||||||
|
this._dashboardWidgetSchema.properties[id] = schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context === undefined || context === 'server') {
|
||||||
|
this._serverWidgetSchema.properties[id] = schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._allSchema.properties[id] = schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
@@ -62,20 +88,24 @@ class DashboardWidgetRegistry implements IDashboardWidgetRegistry {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get databaseWidgetSchema(): IJSONSchema {
|
public get databaseWidgetSchema(): CustomIJSONSchema {
|
||||||
return this._dashboardWidgetSchema;
|
return deepClone(this._dashboardWidgetSchema);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get serverWidgetSchema(): IJSONSchema {
|
public get serverWidgetSchema(): CustomIJSONSchema {
|
||||||
return this._serverWidgetSchema;
|
return deepClone(this._serverWidgetSchema);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get allSchema(): CustomIJSONSchema {
|
||||||
|
return deepClone(this._allSchema);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const dashboardWidgetRegistry = new DashboardWidgetRegistry();
|
const dashboardWidgetRegistry = new DashboardWidgetRegistry();
|
||||||
platform.Registry.add(Extensions.DashboardWidgetContribution, dashboardWidgetRegistry);
|
platform.Registry.add(Extensions.DashboardWidgetContribution, dashboardWidgetRegistry);
|
||||||
|
|
||||||
export function registerDashboardWidget(id: string, description: string, schema: IJSONSchema, context?: 'database' | 'server'): WidgetIdentifier {
|
export function registerDashboardWidget(id: string, description: string, schema: IJSONSchema, context?: 'database' | 'server', options?: IDashboardRegistryOptions): WidgetIdentifier {
|
||||||
return dashboardWidgetRegistry.registerWidget(id, description, schema, context);
|
return dashboardWidgetRegistry.registerWidget(id, description, schema, context, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function registerNonCustomDashboardWidget(id: string, description: string, val: IInsightsConfig): WidgetIdentifier {
|
export function registerNonCustomDashboardWidget(id: string, description: string, val: IInsightsConfig): WidgetIdentifier {
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ export const IAngularEventingService = createDecorator<IAngularEventingService>(
|
|||||||
export enum AngularEventType {
|
export enum AngularEventType {
|
||||||
NAV_DATABASE,
|
NAV_DATABASE,
|
||||||
NAV_SERVER,
|
NAV_SERVER,
|
||||||
DELETE_WIDGET
|
DELETE_WIDGET,
|
||||||
|
PINUNPIN_TAB,
|
||||||
|
NEW_TABS,
|
||||||
|
CLOSE_TAB
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IDeleteWidgetPayload {
|
export interface IDeleteWidgetPayload {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { ISqlOAuthService } from 'sql/common/sqlOAuthService';
|
|||||||
import { IFileBrowserService, IFileBrowserDialogController } from 'sql/parts/fileBrowser/common/interfaces';
|
import { IFileBrowserService, IFileBrowserDialogController } from 'sql/parts/fileBrowser/common/interfaces';
|
||||||
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
|
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
|
||||||
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
|
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
|
||||||
|
import { IDashboardWebviewService } from 'sql/services/dashboardWebview/common/dashboardWebviewService';
|
||||||
|
|
||||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||||
@@ -88,6 +89,7 @@ export interface IBootstrapService {
|
|||||||
clipboardService: IClipboardService;
|
clipboardService: IClipboardService;
|
||||||
capabilitiesService: ICapabilitiesService;
|
capabilitiesService: ICapabilitiesService;
|
||||||
configurationEditorService: ConfigurationEditingService;
|
configurationEditorService: ConfigurationEditingService;
|
||||||
|
dashboardWebviewService: IDashboardWebviewService;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Bootstraps the Angular module described. Components that need singleton services should inject the
|
* Bootstraps the Angular module described. Components that need singleton services should inject the
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import { IWindowsService, IWindowService } from 'vs/platform/windows/common/wind
|
|||||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||||
import { ConfigurationEditingService } from 'vs/workbench/services/configuration/node/configurationEditingService';
|
import { ConfigurationEditingService } from 'vs/workbench/services/configuration/node/configurationEditingService';
|
||||||
|
import { IDashboardWebviewService } from 'sql/services/dashboardWebview/common/dashboardWebviewService';
|
||||||
|
|
||||||
export class BootstrapService implements IBootstrapService {
|
export class BootstrapService implements IBootstrapService {
|
||||||
|
|
||||||
@@ -96,7 +97,8 @@ export class BootstrapService implements IBootstrapService {
|
|||||||
@ITelemetryService public telemetryService: ITelemetryService,
|
@ITelemetryService public telemetryService: ITelemetryService,
|
||||||
@IStorageService public storageService: IStorageService,
|
@IStorageService public storageService: IStorageService,
|
||||||
@IClipboardService public clipboardService: IClipboardService,
|
@IClipboardService public clipboardService: IClipboardService,
|
||||||
@ICapabilitiesService public capabilitiesService: ICapabilitiesService
|
@ICapabilitiesService public capabilitiesService: ICapabilitiesService,
|
||||||
|
@IDashboardWebviewService public dashboardWebviewService: IDashboardWebviewService
|
||||||
) {
|
) {
|
||||||
this.configurationEditorService = this.instantiationService.createInstance(ConfigurationEditingService);
|
this.configurationEditorService = this.instantiationService.createInstance(ConfigurationEditingService);
|
||||||
this._bootstrapParameterMap = new Map<string, BootstrapParams>();
|
this._bootstrapParameterMap = new Map<string, BootstrapParams>();
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
import Event from 'vs/base/common/event';
|
||||||
|
|
||||||
|
import * as data from 'data';
|
||||||
|
|
||||||
|
export const SERVICE_ID = 'dashboardWebviewService';
|
||||||
|
|
||||||
|
export interface IDashboardWebview {
|
||||||
|
readonly id: string;
|
||||||
|
readonly connection: data.connection.Connection;
|
||||||
|
readonly serverInfo: data.ServerInfo;
|
||||||
|
setHtml(html: string): void;
|
||||||
|
onMessage: Event<string>;
|
||||||
|
sendMessage(message: string);
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IDashboardWebviewService {
|
||||||
|
_serviceBrand: any;
|
||||||
|
onRegisteredWebview: Event<IDashboardWebview>;
|
||||||
|
registerWebview(widget: IDashboardWebview);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const IDashboardWebviewService = createDecorator<IDashboardWebviewService>(SERVICE_ID);
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import { IDashboardWebviewService, IDashboardWebview } from 'sql/services/dashboardWebview/common/dashboardWebviewService';
|
||||||
|
import Event, { Emitter } from 'vs/base/common/event';
|
||||||
|
|
||||||
|
export class DashboardWebviewService implements IDashboardWebviewService {
|
||||||
|
_serviceBrand: any;
|
||||||
|
|
||||||
|
private _onRegisteredWebview = new Emitter<IDashboardWebview>();
|
||||||
|
public readonly onRegisteredWebview: Event<IDashboardWebview> = this._onRegisteredWebview.event;
|
||||||
|
|
||||||
|
|
||||||
|
public registerWebview(widget: IDashboardWebview) {
|
||||||
|
this._onRegisteredWebview.fire(widget);
|
||||||
|
}
|
||||||
|
}
|
||||||
94
src/sql/workbench/api/node/extHostDashboardWebview.ts
Normal file
94
src/sql/workbench/api/node/extHostDashboardWebview.ts
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import { SqlMainContext, ExtHostDashboardWebviewsShape, MainThreadDashboardWebviewShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
|
||||||
|
|
||||||
|
import { IMainContext } from 'vs/workbench/api/node/extHost.protocol';
|
||||||
|
import { Emitter } from 'vs/base/common/event';
|
||||||
|
import { deepClone } from 'vs/base/common/objects';
|
||||||
|
|
||||||
|
import * as vscode from 'vscode';
|
||||||
|
import * as data from 'data';
|
||||||
|
|
||||||
|
class ExtHostDashboardWebview implements data.DashboardWebview {
|
||||||
|
|
||||||
|
private _html: string;
|
||||||
|
public onMessageEmitter = new Emitter<any>();
|
||||||
|
public onClosedEmitter = new Emitter<any>();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly _proxy: MainThreadDashboardWebviewShape,
|
||||||
|
private readonly _handle: number,
|
||||||
|
private readonly _connection: data.connection.Connection,
|
||||||
|
private readonly _serverInfo: data.ServerInfo
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public postMessage(message: any): Thenable<any> {
|
||||||
|
return this._proxy.$sendMessage(this._handle, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get onMessage(): vscode.Event<any> {
|
||||||
|
return this.onMessageEmitter.event;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get onClosed(): vscode.Event<any> {
|
||||||
|
return this.onClosedEmitter.event;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get connection(): data.connection.Connection {
|
||||||
|
return deepClone(this._connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get serverInfo(): data.ServerInfo {
|
||||||
|
return deepClone(this._serverInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
get html(): string {
|
||||||
|
return this._html;
|
||||||
|
}
|
||||||
|
|
||||||
|
set html(value: string) {
|
||||||
|
if (this._html !== value) {
|
||||||
|
this._html = value;
|
||||||
|
this._proxy.$setHtml(this._handle, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ExtHostDashboardWebviews implements ExtHostDashboardWebviewsShape {
|
||||||
|
private readonly _proxy: MainThreadDashboardWebviewShape;
|
||||||
|
|
||||||
|
private readonly _webviews = new Map<number, ExtHostDashboardWebview>();
|
||||||
|
private readonly _handlers = new Map<string, (webview: data.DashboardWebview) => void>();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
mainContext: IMainContext
|
||||||
|
) {
|
||||||
|
this._proxy = mainContext.get(SqlMainContext.MainThreadDashboardWebview);
|
||||||
|
}
|
||||||
|
|
||||||
|
$onMessage(handle: number, message: any): void {
|
||||||
|
const webview = this._webviews.get(handle);
|
||||||
|
webview.onMessageEmitter.fire(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
$onClosed(handle: number): void {
|
||||||
|
const webview = this._webviews.get(handle);
|
||||||
|
webview.onClosedEmitter.fire();
|
||||||
|
this._webviews.delete(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
$registerProvider(widgetId: string, handler: (webview: data.DashboardWebview) => void): void {
|
||||||
|
this._handlers.set(widgetId, handler);
|
||||||
|
this._proxy.$registerProvider(widgetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
$registerWidget(handle: number, id: string, connection: data.connection.Connection, serverInfo: data.ServerInfo): void {
|
||||||
|
let webview = new ExtHostDashboardWebview(this._proxy, handle, connection, serverInfo);
|
||||||
|
this._webviews.set(handle, webview);
|
||||||
|
this._handlers.get(id)(webview);
|
||||||
|
}
|
||||||
|
}
|
||||||
53
src/sql/workbench/api/node/mainThreadDashboardWebview.ts
Normal file
53
src/sql/workbench/api/node/mainThreadDashboardWebview.ts
Normal file
@@ -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.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import { MainThreadDashboardWebviewShape, SqlMainContext, ExtHostDashboardWebviewsShape, SqlExtHostContext } from 'sql/workbench/api/node/sqlExtHost.protocol';
|
||||||
|
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||||
|
import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
|
||||||
|
import { IDashboardWebviewService, IDashboardWebview } from 'sql/services/dashboardWebview/common/dashboardWebviewService';
|
||||||
|
|
||||||
|
@extHostNamedCustomer(SqlMainContext.MainThreadDashboardWebview)
|
||||||
|
export class MainThreadDashboardWebview implements MainThreadDashboardWebviewShape {
|
||||||
|
|
||||||
|
private static _handlePool = 0;
|
||||||
|
private readonly _proxy: ExtHostDashboardWebviewsShape;
|
||||||
|
private readonly _dialogs = new Map<number, IDashboardWebview>();
|
||||||
|
|
||||||
|
private knownWidgets = new Array<string>();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
context: IExtHostContext,
|
||||||
|
@IDashboardWebviewService webviewService: IDashboardWebviewService
|
||||||
|
) {
|
||||||
|
this._proxy = context.get(SqlExtHostContext.ExtHostDashboardWebviews);
|
||||||
|
webviewService.onRegisteredWebview(e => {
|
||||||
|
if (this.knownWidgets.includes(e.id)) {
|
||||||
|
let handle = MainThreadDashboardWebview._handlePool++;
|
||||||
|
this._dialogs.set(handle, e);
|
||||||
|
this._proxy.$registerWidget(handle, e.id, e.connection, e.serverInfo);
|
||||||
|
e.onMessage(e => {
|
||||||
|
this._proxy.$onMessage(handle, e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public dispose(): void {
|
||||||
|
throw new Error("Method not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$sendMessage(handle: number, message: string) {
|
||||||
|
this._dialogs.get(handle).sendMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
$setHtml(handle: number, value: string) {
|
||||||
|
this._dialogs.get(handle).setHtml(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
$registerProvider(widgetId: string) {
|
||||||
|
this.knownWidgets.push(widgetId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,7 @@ import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration
|
|||||||
import { ExtHostModalDialogs } from 'sql/workbench/api/node/extHostModalDialog';
|
import { ExtHostModalDialogs } from 'sql/workbench/api/node/extHostModalDialog';
|
||||||
import { ILogService } from 'vs/platform/log/common/log';
|
import { ILogService } from 'vs/platform/log/common/log';
|
||||||
import { IExtensionApiFactory } from 'vs/workbench/api/node/extHost.api.impl';
|
import { IExtensionApiFactory } from 'vs/workbench/api/node/extHost.api.impl';
|
||||||
|
import { ExtHostDashboardWebviews } from 'sql/workbench/api/node/extHostDashboardWebview';
|
||||||
import { ExtHostConnectionManagement } from 'sql/workbench/api/node/extHostConnectionManagement';
|
import { ExtHostConnectionManagement } from 'sql/workbench/api/node/extHostConnectionManagement';
|
||||||
|
|
||||||
export interface ISqlExtensionApiFactory {
|
export interface ISqlExtensionApiFactory {
|
||||||
@@ -56,6 +57,7 @@ export function createApiFactory(
|
|||||||
const extHostSerializationProvider = threadService.set(SqlExtHostContext.ExtHostSerializationProvider, new ExtHostSerializationProvider(threadService));
|
const extHostSerializationProvider = threadService.set(SqlExtHostContext.ExtHostSerializationProvider, new ExtHostSerializationProvider(threadService));
|
||||||
const extHostResourceProvider = threadService.set(SqlExtHostContext.ExtHostResourceProvider, new ExtHostResourceProvider(threadService));
|
const extHostResourceProvider = threadService.set(SqlExtHostContext.ExtHostResourceProvider, new ExtHostResourceProvider(threadService));
|
||||||
const extHostModalDialogs = threadService.set(SqlExtHostContext.ExtHostModalDialogs, new ExtHostModalDialogs(threadService));
|
const extHostModalDialogs = threadService.set(SqlExtHostContext.ExtHostModalDialogs, new ExtHostModalDialogs(threadService));
|
||||||
|
const extHostWebviewWidgets = threadService.set(SqlExtHostContext.ExtHostDashboardWebviews, new ExtHostDashboardWebviews(threadService));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
vsCodeFactory: vsCodeFactory,
|
vsCodeFactory: vsCodeFactory,
|
||||||
@@ -259,6 +261,12 @@ export function createApiFactory(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const dashboard = {
|
||||||
|
registerWebviewProvider(widgetId: string, handler: (webview: data.DashboardWebview) => void) {
|
||||||
|
extHostWebviewWidgets.$registerProvider(widgetId, handler);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
accounts,
|
accounts,
|
||||||
connection,
|
connection,
|
||||||
@@ -273,7 +281,8 @@ export function createApiFactory(
|
|||||||
TaskStatus: sqlExtHostTypes.TaskStatus,
|
TaskStatus: sqlExtHostTypes.TaskStatus,
|
||||||
TaskExecutionMode: sqlExtHostTypes.TaskExecutionMode,
|
TaskExecutionMode: sqlExtHostTypes.TaskExecutionMode,
|
||||||
ScriptOperation: sqlExtHostTypes.ScriptOperation,
|
ScriptOperation: sqlExtHostTypes.ScriptOperation,
|
||||||
window
|
window,
|
||||||
|
dashboard
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import 'sql/workbench/api/node/mainThreadCredentialManagement';
|
|||||||
import 'sql/workbench/api/node/mainThreadDataProtocol';
|
import 'sql/workbench/api/node/mainThreadDataProtocol';
|
||||||
import 'sql/workbench/api/node/mainThreadSerializationProvider';
|
import 'sql/workbench/api/node/mainThreadSerializationProvider';
|
||||||
import 'sql/workbench/api/node/mainThreadResourceProvider';
|
import 'sql/workbench/api/node/mainThreadResourceProvider';
|
||||||
|
import 'sql/workbench/api/node/mainThreadDashboardWebview';
|
||||||
import './mainThreadAccountManagement';
|
import './mainThreadAccountManagement';
|
||||||
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import * as data from 'data';
|
|||||||
|
|
||||||
import { TPromise } from 'vs/base/common/winjs.base';
|
import { TPromise } from 'vs/base/common/winjs.base';
|
||||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||||
|
|
||||||
export abstract class ExtHostAccountManagementShape {
|
export abstract class ExtHostAccountManagementShape {
|
||||||
$autoOAuthCancelled(handle: number): Thenable<void> { throw ni(); }
|
$autoOAuthCancelled(handle: number): Thenable<void> { throw ni(); }
|
||||||
$clear(handle: number, accountKey: data.AccountKey): Thenable<void> { throw ni(); }
|
$clear(handle: number, accountKey: data.AccountKey): Thenable<void> { throw ni(); }
|
||||||
@@ -420,6 +421,7 @@ export const SqlMainContext = {
|
|||||||
MainThreadSerializationProvider: createMainId<MainThreadSerializationProviderShape>('MainThreadSerializationProvider'),
|
MainThreadSerializationProvider: createMainId<MainThreadSerializationProviderShape>('MainThreadSerializationProvider'),
|
||||||
MainThreadResourceProvider: createMainId<MainThreadResourceProviderShape>('MainThreadResourceProvider'),
|
MainThreadResourceProvider: createMainId<MainThreadResourceProviderShape>('MainThreadResourceProvider'),
|
||||||
MainThreadModalDialog: createMainId<MainThreadModalDialogShape>('MainThreadModalDialog'),
|
MainThreadModalDialog: createMainId<MainThreadModalDialogShape>('MainThreadModalDialog'),
|
||||||
|
MainThreadDashboardWebview: createMainId<MainThreadDashboardWebviewShape>('MainThreadDashboardWebview')
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SqlExtHostContext = {
|
export const SqlExtHostContext = {
|
||||||
@@ -429,7 +431,8 @@ export const SqlExtHostContext = {
|
|||||||
ExtHostDataProtocol: createExtId<ExtHostDataProtocolShape>('ExtHostDataProtocol'),
|
ExtHostDataProtocol: createExtId<ExtHostDataProtocolShape>('ExtHostDataProtocol'),
|
||||||
ExtHostSerializationProvider: createExtId<ExtHostSerializationProviderShape>('ExtHostSerializationProvider'),
|
ExtHostSerializationProvider: createExtId<ExtHostSerializationProviderShape>('ExtHostSerializationProvider'),
|
||||||
ExtHostResourceProvider: createExtId<ExtHostResourceProviderShape>('ExtHostResourceProvider'),
|
ExtHostResourceProvider: createExtId<ExtHostResourceProviderShape>('ExtHostResourceProvider'),
|
||||||
ExtHostModalDialogs: createExtId<ExtHostModalDialogsShape>('ExtHostModalDialogs')
|
ExtHostModalDialogs: createExtId<ExtHostModalDialogsShape>('ExtHostModalDialogs'),
|
||||||
|
ExtHostDashboardWebviews: createExtId<ExtHostDashboardWebviewsShape>('ExtHostDashboardWebviews')
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface MainThreadModalDialogShape extends IDisposable {
|
export interface MainThreadModalDialogShape extends IDisposable {
|
||||||
@@ -440,7 +443,21 @@ export interface MainThreadModalDialogShape extends IDisposable {
|
|||||||
$setHtml(handle: number, value: string): void;
|
$setHtml(handle: number, value: string): void;
|
||||||
$sendMessage(handle: number, value: any): Thenable<boolean>;
|
$sendMessage(handle: number, value: any): Thenable<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExtHostModalDialogsShape {
|
export interface ExtHostModalDialogsShape {
|
||||||
$onMessage(handle: number, message: any): void;
|
$onMessage(handle: number, message: any): void;
|
||||||
$onClosed(handle: number): void;
|
$onClosed(handle: number): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ExtHostDashboardWebviewsShape {
|
||||||
|
$registerProvider(widgetId: string, handler: (webview: data.DashboardWebview) => void): void;
|
||||||
|
$onMessage(handle: number, message: any): void;
|
||||||
|
$onClosed(handle: number): void;
|
||||||
|
$registerWidget(handle: number, id: string, connection: data.connection.Connection, serverInfo: data.ServerInfo): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MainThreadDashboardWebviewShape extends IDisposable {
|
||||||
|
$sendMessage(handle: number, message: string);
|
||||||
|
$registerProvider(widgetId: string);
|
||||||
|
$setHtml(handle: number, value: string);
|
||||||
|
}
|
||||||
|
|||||||
@@ -127,6 +127,8 @@ import { IBackupService, IBackupUiService } from 'sql/parts/disasterRecovery/bac
|
|||||||
import { BackupService, BackupUiService } from 'sql/parts/disasterRecovery/backup/common/backupServiceImp';
|
import { BackupService, BackupUiService } from 'sql/parts/disasterRecovery/backup/common/backupServiceImp';
|
||||||
import { IRestoreDialogController, IRestoreService } from 'sql/parts/disasterRecovery/restore/common/restoreService';
|
import { IRestoreDialogController, IRestoreService } from 'sql/parts/disasterRecovery/restore/common/restoreService';
|
||||||
import { RestoreService, RestoreDialogController } from 'sql/parts/disasterRecovery/restore/common/restoreServiceImpl';
|
import { RestoreService, RestoreDialogController } from 'sql/parts/disasterRecovery/restore/common/restoreServiceImpl';
|
||||||
|
import { INewDashboardTabDialogService } from 'sql/parts/dashboard/newDashboardTabDialog/interface';
|
||||||
|
import { NewDashboardTabDialogService } from 'sql/parts/dashboard/newDashboardTabDialog/newDashboardTabDialogService';
|
||||||
import { IFileBrowserService, IFileBrowserDialogController } from 'sql/parts/fileBrowser/common/interfaces';
|
import { IFileBrowserService, IFileBrowserDialogController } from 'sql/parts/fileBrowser/common/interfaces';
|
||||||
import { FileBrowserService } from 'sql/parts/fileBrowser/common/fileBrowserService';
|
import { FileBrowserService } from 'sql/parts/fileBrowser/common/fileBrowserService';
|
||||||
import { FileBrowserDialogController } from 'sql/parts/fileBrowser/fileBrowserDialogController';
|
import { FileBrowserDialogController } from 'sql/parts/fileBrowser/fileBrowserDialogController';
|
||||||
@@ -143,6 +145,8 @@ import { ClipboardService as sqlClipboardService } from 'sql/platform/clipboard/
|
|||||||
import { IResourceProviderService, IAccountPickerService } from 'sql/parts/accountManagement/common/interfaces';
|
import { IResourceProviderService, IAccountPickerService } from 'sql/parts/accountManagement/common/interfaces';
|
||||||
import { ResourceProviderService } from 'sql/parts/accountManagement/common/resourceProviderService';
|
import { ResourceProviderService } from 'sql/parts/accountManagement/common/resourceProviderService';
|
||||||
import { AccountPickerService } from 'sql/parts/accountManagement/accountPicker/accountPickerService';
|
import { AccountPickerService } from 'sql/parts/accountManagement/accountPicker/accountPickerService';
|
||||||
|
import { IDashboardWebviewService } from 'sql/services/dashboardWebview/common/dashboardWebviewService';
|
||||||
|
import { DashboardWebviewService } from 'sql/services/dashboardWebview/common/dashboardWebviewServiceImpl';
|
||||||
|
|
||||||
export const MessagesVisibleContext = new RawContextKey<boolean>('globalMessageVisible', false);
|
export const MessagesVisibleContext = new RawContextKey<boolean>('globalMessageVisible', false);
|
||||||
export const EditorsVisibleContext = new RawContextKey<boolean>('editorIsOpen', false);
|
export const EditorsVisibleContext = new RawContextKey<boolean>('editorIsOpen', false);
|
||||||
@@ -670,7 +674,9 @@ export class Workbench implements IPartService {
|
|||||||
|
|
||||||
// {{SQL CARBON EDIT}}
|
// {{SQL CARBON EDIT}}
|
||||||
// SQL Tools services
|
// SQL Tools services
|
||||||
|
serviceCollection.set(IDashboardWebviewService, this.instantiationService.createInstance(DashboardWebviewService));
|
||||||
serviceCollection.set(IAngularEventingService, this.instantiationService.createInstance(AngularEventingService));
|
serviceCollection.set(IAngularEventingService, this.instantiationService.createInstance(AngularEventingService));
|
||||||
|
serviceCollection.set(INewDashboardTabDialogService, this.instantiationService.createInstance(NewDashboardTabDialogService));
|
||||||
serviceCollection.set(ISqlOAuthService, this.instantiationService.createInstance(SqlOAuthService));
|
serviceCollection.set(ISqlOAuthService, this.instantiationService.createInstance(SqlOAuthService));
|
||||||
serviceCollection.set(sqlIClipboardService, this.instantiationService.createInstance(sqlClipboardService));
|
serviceCollection.set(sqlIClipboardService, this.instantiationService.createInstance(sqlClipboardService));
|
||||||
serviceCollection.set(ICapabilitiesService, this.instantiationService.createInstance(CapabilitiesService));
|
serviceCollection.set(ICapabilitiesService, this.instantiationService.createInstance(CapabilitiesService));
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ export default class Webview {
|
|||||||
if (parent) {
|
if (parent) {
|
||||||
// {{SQL CARBON EDIT}}
|
// {{SQL CARBON EDIT}}
|
||||||
if (!this._options.hideFind) {
|
if (!this._options.hideFind) {
|
||||||
parent.appendChild(this._webviewFindWidget.getDomNode());
|
parent.appendChild(this._webviewFindWidget.getDomNode());
|
||||||
}
|
}
|
||||||
parent.appendChild(this._webview);
|
parent.appendChild(this._webview);
|
||||||
}
|
}
|
||||||
@@ -215,8 +215,11 @@ export default class Webview {
|
|||||||
|
|
||||||
if (this._webview.parentElement) {
|
if (this._webview.parentElement) {
|
||||||
this._webview.parentElement.removeChild(this._webview);
|
this._webview.parentElement.removeChild(this._webview);
|
||||||
const findWidgetDomNode = this._webviewFindWidget.getDomNode();
|
// {{SQL CARBON EDIT}}
|
||||||
findWidgetDomNode.parentElement.removeChild(findWidgetDomNode);
|
if (!this._options.hideFind) {
|
||||||
|
const findWidgetDomNode = this._webviewFindWidget.getDomNode();
|
||||||
|
findWidgetDomNode.parentElement.removeChild(findWidgetDomNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -157,7 +157,12 @@ import 'sql/parts/dashboard/widgets/insights/views/imageInsight.contribution';
|
|||||||
import 'sql/parts/dashboard/widgets/insights/insightsWidget.contribution';
|
import 'sql/parts/dashboard/widgets/insights/insightsWidget.contribution';
|
||||||
import 'sql/parts/dashboard/widgets/explorer/explorerWidget.contribution';
|
import 'sql/parts/dashboard/widgets/explorer/explorerWidget.contribution';
|
||||||
import 'sql/parts/dashboard/widgets/tasks/tasksWidget.contribution';
|
import 'sql/parts/dashboard/widgets/tasks/tasksWidget.contribution';
|
||||||
|
import 'sql/parts/dashboard/widgets/webview/webviewWidget.contribution';
|
||||||
import 'sql/parts/dashboard/dashboardConfig.contribution';
|
import 'sql/parts/dashboard/dashboardConfig.contribution';
|
||||||
|
/* Tabs */
|
||||||
|
import 'sql/parts/dashboard/tabs/dashboardWebviewTab.contribution';
|
||||||
|
import 'sql/parts/dashboard/tabs/dashboardWidgetTab.contribution';
|
||||||
|
import 'sql/parts/dashboard/common/dashboardTab.contribution';
|
||||||
/* Tasks */
|
/* Tasks */
|
||||||
import 'sql/workbench/common/actions.contribution';
|
import 'sql/workbench/common/actions.contribution';
|
||||||
/* Extension Host */
|
/* Extension Host */
|
||||||
|
|||||||
Reference in New Issue
Block a user