diff --git a/src/sql/base/browser/ui/panel/media/panel.css b/src/sql/base/browser/ui/panel/media/panel.css index 1df983cdf5..e3f4fd9051 100644 --- a/src/sql/base/browser/ui/panel/media/panel.css +++ b/src/sql/base/browser/ui/panel/media/panel.css @@ -29,6 +29,8 @@ panel { padding: 0; justify-content: flex-start; line-height: 35px; + white-space: nowrap; + flex: 1; } .tabbedPanel .tabList .tab { @@ -61,7 +63,7 @@ panel { } .composite.title .title-actions { - flex: 1; + flex: 1 1 auto; } .tab > .tabLabel.active { @@ -87,6 +89,10 @@ panel { height: 100%; } +.tabbedPanel.vertical .tabList { + flex-direction: column; +} + .tabbedPanel > .tab-content { flex: 1; position: relative; @@ -98,4 +104,9 @@ panel { .tabbedPanel.horizontal > .title > .tabList { flex-flow: row; +} + +.tabbedPanel > .title > .monaco-scrollable-element { + flex: 0 1 auto; + width: inherit; } \ No newline at end of file diff --git a/src/sql/base/browser/ui/panel/panel.component.ts b/src/sql/base/browser/ui/panel/panel.component.ts index fd8d9bc7ca..52ba175477 100644 --- a/src/sql/base/browser/ui/panel/panel.component.ts +++ b/src/sql/base/browser/ui/panel/panel.component.ts @@ -13,6 +13,10 @@ 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 { mixin } from 'vs/base/common/objects'; +import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { ScrollbarVisibility } from 'vs/base/common/scrollable'; +import { addDisposableListener, EventType } from 'vs/base/browser/dom'; +import { Disposable } from 'vs/base/common/lifecycle'; export interface IPanelOptions { /** @@ -40,9 +44,10 @@ let idPool = 0; @Component({ selector: 'panel', template: ` +
-
-
+
+
@@ -60,7 +65,7 @@ let idPool = 0;
` }) -export class PanelComponent implements AfterContentInit, OnInit, OnChanges, OnDestroy, AfterViewInit { +export class PanelComponent extends Disposable implements AfterContentInit, OnInit, OnChanges, OnDestroy, AfterViewInit { @Input() public options: IPanelOptions; @Input() public actions: Array; @ContentChildren(TabComponent) private _tabs: QueryList; @@ -72,10 +77,15 @@ export class PanelComponent implements AfterContentInit, OnInit, OnChanges, OnDe private _activeTab: TabComponent; private _actionbar: ActionBar; private _mru: TabComponent[]; + private _scrollableElement: ScrollableElement; @ViewChild('panelActionbar', { read: ElementRef }) private _actionbarRef: ElementRef; @ViewChild('tabbedPanel', { read: ElementRef }) private _tabbedPanelRef: ElementRef; - constructor( @Inject(forwardRef(() => NgZone)) private _zone: NgZone) { } + @ViewChild('titleContainer', { read: ElementRef }) private _titleContainer: ElementRef; + @ViewChild('tabList', { read: ElementRef }) private _tabList: ElementRef; + constructor( @Inject(forwardRef(() => NgZone)) private _zone: NgZone) { + super(); + } ngOnInit(): void { this.options = mixin(this.options || {}, defaultOptions, false); @@ -90,6 +100,39 @@ export class PanelComponent implements AfterContentInit, OnInit, OnChanges, OnDe } ngAfterViewInit(): void { + let container = this._titleContainer.nativeElement as HTMLElement; + let tabList = this._tabList.nativeElement as HTMLElement; + container.removeChild(tabList); + + this._scrollableElement = new ScrollableElement(tabList, { + horizontal: ScrollbarVisibility.Auto, + vertical: ScrollbarVisibility.Hidden, + scrollYToX: true, + useShadows: false, + horizontalScrollbarSize: 3 + }); + + this._scrollableElement.onScroll(e => { + tabList.scrollLeft = e.scrollLeft; + }); + + container.insertBefore(this._scrollableElement.getDomNode(), container.firstChild); + + this._scrollableElement.setScrollDimensions({ + width: tabList.offsetWidth, + scrollWidth: tabList.scrollWidth + }); + + this._register(addDisposableListener(window, EventType.RESIZE, () => { + // Todo: Need to set timeout because we have to make sure that the grids have already rearraged before the getContentHeight gets called. + setTimeout(() => { + this._scrollableElement.setScrollDimensions({ + width: tabList.offsetWidth, + scrollWidth: tabList.scrollWidth + }); + }, 100); + })); + if (this.options.layout === NavigationBarLayout.horizontal) { (this._tabbedPanelRef.nativeElement).classList.add(horizontalLayout); } else { diff --git a/src/sql/parts/dashboard/common/dashboardPage.component.ts b/src/sql/parts/dashboard/common/dashboardPage.component.ts index bc961bb7ea..957a5b21fb 100644 --- a/src/sql/parts/dashboard/common/dashboardPage.component.ts +++ b/src/sql/parts/dashboard/common/dashboardPage.component.ts @@ -174,7 +174,7 @@ export abstract class DashboardPage extends Disposable implements OnDestroy { })); this._tabsDispose.push(this.dashboardService.onAddNewTabs(e => { - this.loadNewTabs(e); + this.loadNewTabs(e, true); })); } @@ -188,7 +188,7 @@ export abstract class DashboardPage extends Disposable implements OnDestroy { this.dashboardService.writeSettings([this.context, 'tabs'].join('.'), writeableConfig, target); } - private loadNewTabs(dashboardTabs: IDashboardTab[]) { + private loadNewTabs(dashboardTabs: IDashboardTab[], openLastTab: boolean = false) { if (dashboardTabs && dashboardTabs.length > 0) { let selectedTabs = dashboardTabs.map(v => { let container = dashboardHelper.getDashboardContainer(v.container); @@ -226,10 +226,12 @@ export abstract class DashboardPage extends Disposable implements OnDestroy { return config; }); - // put this immediately on the stack so that is ran *after* the tab is rendered - setTimeout(() => { - this._panel.selectTab(selectedTabs.pop().id); - }); + if (openLastTab) { + // put this immediately on the stack so that is ran *after* the tab is rendered + setTimeout(() => { + this._panel.selectTab(selectedTabs.pop().id); + }); + } } } diff --git a/src/sql/parts/dashboard/common/dashboardPanel.css b/src/sql/parts/dashboard/common/dashboardPanel.css index 5b3f82987e..5de4d65f2c 100644 --- a/src/sql/parts/dashboard/common/dashboardPanel.css +++ b/src/sql/parts/dashboard/common/dashboardPanel.css @@ -9,12 +9,12 @@ panel.dashboard-panel > .tabbedPanel { box-sizing: border-box; } -panel.dashboard-panel > .tabbedPanel > .title > .tabList .tab-header .tab > .tabLabel.active { +panel.dashboard-panel > .tabbedPanel > .title > .monaco-scrollable-element > .tabList .tab-header .tab > .tabLabel.active { border-bottom: 0px solid; } panel.dashboard-panel > .tabbedPanel > .title > .title-actions, -panel.dashboard-panel > .tabbedPanel > .title > .tabList .tab-header { +panel.dashboard-panel > .tabbedPanel > .title > .monaco-scrollable-element > .tabList .tab-header { box-sizing: border-box; border: 1px solid transparent; } \ No newline at end of file diff --git a/src/sql/parts/dashboard/common/dashboardPanelStyles.ts b/src/sql/parts/dashboard/common/dashboardPanelStyles.ts index b77c1265cd..6b9bdcbf76 100644 --- a/src/sql/parts/dashboard/common/dashboardPanelStyles.ts +++ b/src/sql/parts/dashboard/common/dashboardPanelStyles.ts @@ -15,21 +15,22 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { 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 { + panel.dashboard-panel > .tabbedPanel > .title > .monaco-scrollable-element > .tabList .tab:hover .tabLabel, + panel.dashboard-panel > .tabbedPanel > .title > .monaco-scrollable-element > .tabList .tab .tabLabel.active { color: ${tabActiveForeground}; border-bottom: 0px solid; } - panel.dashboard-panel > .tabbedPanel > .title > .tabList .tab-header.active { + panel.dashboard-panel > .tabbedPanel > .title > .monaco-scrollable-element > .tabList .tab-header.active { background-color: ${tabActiveBackground}; + outline-color: ${tabActiveBackground}; } - panel.dashboard-panel > .tabbedPanel.horizontal > .title > .tabList .tab-header.active { + panel.dashboard-panel > .tabbedPanel.horizontal > .title > .monaco-scrollable-element > .tabList .tab-header.active { border-bottom-color: transparent; } - panel.dashboard-panel > .tabbedPanel.vertical > .title > .tabList .tab-header.active { + panel.dashboard-panel > .tabbedPanel.vertical > .title > .monaco-scrollable-element > .tabList .tab-header.active { border-right-color: transparent; } `); @@ -38,7 +39,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const activeTabBorderColor = theme.getColor(TAB_ACTIVE_BORDER); if (activeTabBorderColor) { collector.addRule(` - panel.dashboard-panel > .tabbedPanel > .title > .tabList .tab-header.active { + panel.dashboard-panel > .tabbedPanel > .title > .monaco-scrollable-element > .tabList .tab-header.active { box-shadow: ${activeTabBorderColor} 0 -1px inset; } `); @@ -49,11 +50,11 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const tabInactiveForeground = theme.getColor(TAB_INACTIVE_FOREGROUND); if (tabInactiveBackground || tabInactiveForeground) { collector.addRule(` - panel.dashboard-panel > .tabbedPanel > .title > .tabList .tab .tabLabel { + panel.dashboard-panel > .tabbedPanel > .title > .monaco-scrollable-element > .tabList .tab .tabLabel { color: ${tabInactiveForeground}; } - panel.dashboard-panel > .tabbedPanel > .title > .tabList .tab-header { + panel.dashboard-panel > .tabbedPanel > .title > .monaco-scrollable-element > .tabList .tab-header { background-color: ${tabInactiveBackground}; } `); @@ -73,18 +74,10 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const tabBoarder = theme.getColor(TAB_BORDER); if (tabBoarder) { collector.addRule(` - panel.dashboard-panel > .tabbedPanel > .title > .tabList .tab-header { + panel.dashboard-panel > .tabbedPanel > .title > .monaco-scrollable-element > .tabList .tab-header { border-right-color: ${tabBoarder}; border-bottom-color: ${tabBoarder}; } - - panel.dashboard-panel > .tabbedPanel.horizontal > .title > .title-actions { - border-bottom-color: ${tabBoarder}; - } - - panel.dashboard-panel > .tabbedPanel.vertical > .title > .title-actions { - border-right-color: ${tabBoarder}; - } `); }