mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-22 09:35:37 -05:00
SQL Operations Studio Public Preview 1 (0.23) release source code
This commit is contained in:
58
src/sql/base/browser/ui/panel/media/panel.css
Normal file
58
src/sql/base/browser/ui/panel/media/panel.css
Normal file
@@ -0,0 +1,58 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.tabbedPanel {
|
||||
border-top-color: rgba(128, 128, 128, 0.35);
|
||||
border-top-width: 1;
|
||||
border-top-style: solid;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tabbedPanel .composite.title {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.tabbedPanel .tabList {
|
||||
display: flex;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
justify-content: flex-start;
|
||||
flex-flow: row;
|
||||
line-height: 35px;
|
||||
}
|
||||
|
||||
.tabbedPanel .tabList > .tab {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tabbedPanel .tabList > .tab > .tabLabel {
|
||||
text-transform: uppercase;
|
||||
margin-left: 16px;
|
||||
margin-right: 16px;
|
||||
font-size: 11px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.tabbedPanel .composite.title .title-actions .action-label {
|
||||
display: block;
|
||||
height: 35px;
|
||||
line-height: 35px;
|
||||
min-width: 28px;
|
||||
background-size: 16px;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.composite.title .title-actions {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.tab > .tabLabel.active {
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
|
||||
.composite.title ~ tab.fullsize > :first-child {
|
||||
height: calc(100% - 38px);
|
||||
}
|
||||
98
src/sql/base/browser/ui/panel/panel.component.ts
Normal file
98
src/sql/base/browser/ui/panel/panel.component.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* 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 { TabComponent } from './tab.component';
|
||||
import './panelStyles';
|
||||
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
|
||||
export interface IPanelOptions {
|
||||
/**
|
||||
* Whether or not to show the tabs if there is only one tab present
|
||||
*/
|
||||
showTabsWhenOne?: boolean;
|
||||
}
|
||||
|
||||
const defaultOptions: IPanelOptions = {
|
||||
showTabsWhenOne: true
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: 'panel',
|
||||
template: `
|
||||
<div class="tabbedPanel fullsize">
|
||||
<div *ngIf="!options.showTabsWhenOne ? _tabs.length !== 1 : true" class="composite title">
|
||||
<div class="tabList">
|
||||
<div *ngFor="let tab of _tabs" class="tab" (click)="selectTab(tab)">
|
||||
<a class="tabLabel" [class.active]="tab.active">
|
||||
{{tab.title}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="title-actions">
|
||||
</div>
|
||||
</div>
|
||||
<ng-content class="fullsize"></ng-content>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
export class PanelComponent implements AfterContentInit, OnInit {
|
||||
@Input() public options: IPanelOptions;
|
||||
@ContentChildren(TabComponent) private _tabs: QueryList<TabComponent>;
|
||||
private _activeTab: TabComponent;
|
||||
|
||||
constructor( @Inject(forwardRef(() => NgZone)) private _zone: NgZone) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.options = mixin(this.options || {}, defaultOptions, false);
|
||||
}
|
||||
|
||||
ngAfterContentInit(): void {
|
||||
if (this._tabs && this._tabs.length > 0) {
|
||||
this._activeTab = this._tabs.first;
|
||||
this._activeTab.active = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a tab based on index (unrecommended)
|
||||
* @param index index of tab in the html
|
||||
*/
|
||||
selectTab(index: number)
|
||||
/**
|
||||
* Select a tab based on the identifier that was passed into the tab
|
||||
* @param identifier specified identifer of the tab
|
||||
*/
|
||||
selectTab(identifier: string);
|
||||
/**
|
||||
* Select a tab directly if you have access to the object
|
||||
* @param tab tab to navigate to
|
||||
*/
|
||||
selectTab(tab: TabComponent);
|
||||
selectTab(input: TabComponent | number | string) {
|
||||
if (this._tabs && this._tabs.length > 0) {
|
||||
let tab: TabComponent;
|
||||
if (input instanceof TabComponent) {
|
||||
tab = input;
|
||||
} else if (types.isNumber(input)) {
|
||||
tab = this._tabs[input];
|
||||
} else if (types.isString(input)) {
|
||||
tab = this._tabs.find(i => i.identifier === input);
|
||||
}
|
||||
|
||||
this._zone.run(() => {
|
||||
if (this._activeTab) {
|
||||
this._activeTab.active = false;
|
||||
}
|
||||
|
||||
this._activeTab = tab;
|
||||
this._activeTab.active = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/sql/base/browser/ui/panel/panel.module.ts
Normal file
16
src/sql/base/browser/ui/panel/panel.module.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { TabComponent } from './tab.component';
|
||||
import { PanelComponent } from './panel.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule],
|
||||
exports: [TabComponent, PanelComponent],
|
||||
declarations: [TabComponent, PanelComponent]
|
||||
})
|
||||
export class PanelModule { }
|
||||
166
src/sql/base/browser/ui/panel/panel.ts
Normal file
166
src/sql/base/browser/ui/panel/panel.ts
Normal file
@@ -0,0 +1,166 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IThemable } from 'vs/platform/theme/common/styler';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { Dimension, Builder } from 'vs/base/browser/builder';
|
||||
import { addDisposableListener, EventType } from 'vs/base/browser/dom';
|
||||
import { IAction, IActionRunner, Action, IActionChangeEvent, ActionRunner } from 'vs/base/common/actions';
|
||||
import { IActionOptions, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import './panelStyles';
|
||||
|
||||
export interface IPanelStyles {
|
||||
|
||||
}
|
||||
|
||||
export interface IPanelView {
|
||||
render(container: HTMLElement): void;
|
||||
layout(dimension: Dimension): void;
|
||||
}
|
||||
|
||||
export interface IPanelTab {
|
||||
title: string;
|
||||
identifier: string;
|
||||
view: IPanelView;
|
||||
}
|
||||
|
||||
interface IInternalPanelTab extends IPanelTab {
|
||||
header: HTMLElement;
|
||||
label: HTMLElement;
|
||||
}
|
||||
|
||||
export type PanelTabIdentifier = string;
|
||||
|
||||
export class TabbedPanel implements IThemable {
|
||||
private _tabMap = new Map<PanelTabIdentifier, IInternalPanelTab>();
|
||||
private _shownTab: PanelTabIdentifier;
|
||||
public readonly headersize = 35;
|
||||
private _header: HTMLElement;
|
||||
private _tabList: HTMLElement;
|
||||
private _actionbar: ActionBar;
|
||||
private _body: HTMLElement;
|
||||
private _currentDimensions: Dimension;
|
||||
private _collapsed = false;
|
||||
private _parent: HTMLElement;
|
||||
|
||||
private _onTabChange = new Emitter<PanelTabIdentifier>();
|
||||
public onTabChange: Event<PanelTabIdentifier> = this._onTabChange.event;
|
||||
|
||||
constructor(private container: HTMLElement) {
|
||||
this._parent = document.createElement('div');
|
||||
this._parent.className = 'tabbedPanel';
|
||||
container.appendChild(this._parent);
|
||||
this._header = document.createElement('div');
|
||||
this._header.className = 'composite title';
|
||||
this._tabList = document.createElement('div');
|
||||
this._tabList.className = 'tabList';
|
||||
this._tabList.style.height = this.headersize + 'px';
|
||||
this._header.appendChild(this._tabList);
|
||||
let actionbarcontainer = document.createElement('div');
|
||||
actionbarcontainer.className = 'title-actions';
|
||||
this._actionbar = new ActionBar(actionbarcontainer);
|
||||
this._header.appendChild(actionbarcontainer);
|
||||
this._parent.appendChild(this._header);
|
||||
this._body = document.createElement('div');
|
||||
this._body.className = 'tabBody';
|
||||
this._parent.appendChild(this._body);
|
||||
}
|
||||
|
||||
public pushTab(tab: IPanelTab): PanelTabIdentifier {
|
||||
let internalTab = objects.clone(tab) as IInternalPanelTab;
|
||||
this._tabMap.set(tab.identifier, internalTab);
|
||||
this._createTab(internalTab);
|
||||
if (!this._shownTab) {
|
||||
this.showTab(tab.identifier);
|
||||
}
|
||||
return tab.identifier as PanelTabIdentifier;
|
||||
}
|
||||
|
||||
public pushAction(arg: IAction | IAction[], options: IActionOptions = {}): void {
|
||||
this._actionbar.push(arg, options);
|
||||
}
|
||||
|
||||
public set actionBarContext(context: any) {
|
||||
this._actionbar.context = context;
|
||||
}
|
||||
|
||||
private _createTab(tab: IInternalPanelTab): void {
|
||||
let tabElement = document.createElement('div');
|
||||
tabElement.className = 'tab';
|
||||
let tabLabel = document.createElement('a');
|
||||
tabLabel.className = 'tabLabel';
|
||||
tabLabel.innerText = tab.title;
|
||||
tabElement.appendChild(tabLabel);
|
||||
addDisposableListener(tabElement, EventType.CLICK, (e) => this.showTab(tab.identifier));
|
||||
this._tabList.appendChild(tabElement);
|
||||
tab.header = tabElement;
|
||||
tab.label = tabLabel;
|
||||
}
|
||||
|
||||
public showTab(id: PanelTabIdentifier): void {
|
||||
if (this._shownTab && this._shownTab === id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._shownTab) {
|
||||
this._tabMap.get(this._shownTab).label.classList.remove('active');
|
||||
}
|
||||
|
||||
this._shownTab = id;
|
||||
new Builder(this._body).empty();
|
||||
let tab = this._tabMap.get(this._shownTab);
|
||||
tab.label.classList.add('active');
|
||||
tab.view.render(this._body);
|
||||
this._onTabChange.fire(id);
|
||||
if (this._currentDimensions) {
|
||||
this._layoutCurrentTab(new Dimension(this._currentDimensions.width, this._currentDimensions.height - this.headersize));
|
||||
}
|
||||
}
|
||||
|
||||
public removeTab(tab: PanelTabIdentifier) {
|
||||
this._tabMap.get(tab).header.remove();
|
||||
this._tabMap.delete(tab);
|
||||
}
|
||||
|
||||
public style(styles: IPanelStyles): void {
|
||||
|
||||
}
|
||||
|
||||
public layout(dimension: Dimension): void {
|
||||
this._currentDimensions = dimension;
|
||||
this._header.style.width = dimension.width + 'px';
|
||||
this._body.style.width = dimension.width + 'px';
|
||||
this._body.style.height = (dimension.height - this.headersize) + 'px';
|
||||
this._layoutCurrentTab(new Dimension(dimension.width, dimension.height - this.headersize));
|
||||
}
|
||||
|
||||
private _layoutCurrentTab(dimension: Dimension): void {
|
||||
if (this._shownTab) {
|
||||
this._tabMap.get(this._shownTab).view.layout(dimension);
|
||||
}
|
||||
}
|
||||
|
||||
public focus(): void {
|
||||
|
||||
}
|
||||
|
||||
public set collapsed(val: boolean) {
|
||||
if (val === this._collapsed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._collapsed = val === false ? false : true;
|
||||
if (this.collapsed) {
|
||||
this._body.remove();
|
||||
} else {
|
||||
this._parent.appendChild(this._body);
|
||||
}
|
||||
}
|
||||
|
||||
public get collapsed(): boolean {
|
||||
return this._collapsed;
|
||||
}
|
||||
}
|
||||
70
src/sql/base/browser/ui/panel/panelStyles.ts
Normal file
70
src/sql/base/browser/ui/panel/panelStyles.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./media/panel';
|
||||
|
||||
import { registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
|
||||
import { PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_INACTIVE_TITLE_FOREGROUND, PANEL_ACTIVE_TITLE_BORDER } from 'vs/workbench/common/theme';
|
||||
import { activeContrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
|
||||
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
|
||||
// Title Active
|
||||
const titleActive = theme.getColor(PANEL_ACTIVE_TITLE_FOREGROUND);
|
||||
const titleActiveBorder = theme.getColor(PANEL_ACTIVE_TITLE_BORDER);
|
||||
if (titleActive || titleActiveBorder) {
|
||||
collector.addRule(`
|
||||
.tabbedPanel > .title > .tabList > .tab:hover .tabLabel,
|
||||
.tabbedPanel > .title > .tabList > .tab .tabLabel.active {
|
||||
color: ${titleActive};
|
||||
border-bottom-color: ${titleActiveBorder};
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
// Title Inactive
|
||||
const titleInactive = theme.getColor(PANEL_INACTIVE_TITLE_FOREGROUND);
|
||||
if (titleInactive) {
|
||||
collector.addRule(`
|
||||
.tabbedPanel > .title > .tabList > .tab .tabLabel {
|
||||
color: ${titleInactive};
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
// Title focus
|
||||
const focusBorderColor = theme.getColor(focusBorder);
|
||||
if (focusBorderColor) {
|
||||
collector.addRule(`
|
||||
.tabbedPanel > .title > .tabList > .tab .tabLabel:focus {
|
||||
color: ${titleActive};
|
||||
border-bottom-color: ${focusBorderColor} !important;
|
||||
border-bottom: 1px solid;
|
||||
outline: none;
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
// Styling with Outline color (e.g. high contrast theme)
|
||||
const outline = theme.getColor(activeContrastBorder);
|
||||
if (outline) {
|
||||
const outline = theme.getColor(activeContrastBorder);
|
||||
|
||||
collector.addRule(`
|
||||
.tabbedPanel > .title > .tabList > .tab .tabLabel.active,
|
||||
.tabbedPanel > .title > .tabList > .tab .tabLabel:hover {
|
||||
outline-color: ${outline};
|
||||
outline-width: 1px;
|
||||
outline-style: solid;
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
.tabbedPanel > .title > .tabList > .tab .tabLabel:hover:not(.active) {
|
||||
outline-style: dashed;
|
||||
}
|
||||
`);
|
||||
}
|
||||
});
|
||||
35
src/sql/base/browser/ui/panel/tab.component.ts
Normal file
35
src/sql/base/browser/ui/panel/tab.component.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { Component, Input, ContentChild } from '@angular/core';
|
||||
|
||||
export abstract class TabChild {
|
||||
public abstract layout(): void;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'tab',
|
||||
template: `
|
||||
<div *ngIf="active" class="fullsize">
|
||||
<ng-content class="body fullsize"></ng-content>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
export class TabComponent {
|
||||
@ContentChild(TabChild) private _child: TabChild;
|
||||
@Input() public title: string;
|
||||
public _active = false;
|
||||
@Input() public identifier: string;
|
||||
|
||||
public set active(val: boolean) {
|
||||
this._active = val;
|
||||
if (this.active && this._child) {
|
||||
this._child.layout();
|
||||
}
|
||||
}
|
||||
|
||||
public get active(): boolean {
|
||||
return this._active;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user