mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Fixing tabbing logic for tab headers (#19770)
* Fixing tabbing logic for tab headers * Renaming stuff Making null checks concise Adding comments * Renaming css class and interfaces from active to selected * Renaming styling classes and objects * Changing tabbing logic to match w3 behavior * Fixing focus logic in tab * Adding helper comment * Code cleanup
This commit is contained in:
@@ -70,15 +70,15 @@ panel {
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabbedPanel .tabList .tab-header:hover:not(.active) {
|
.tabbedPanel .tabList .tab-header:hover:not(.selected) {
|
||||||
background-color: #dcdcdc;
|
background-color: #dcdcdc;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
.vs-dark .tabbedPanel .tabList .tab-header:hover:not(.active) {
|
.vs-dark .tabbedPanel .tabList .tab-header:hover:not(.selected) {
|
||||||
background-color: #2a2d2e;
|
background-color: #2a2d2e;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
.hc-black .tabbedPanel .tabList .tab-header:hover:not(.active) {
|
.hc-black .tabbedPanel .tabList .tab-header:hover:not(.selected) {
|
||||||
background-color: initial;
|
background-color: initial;
|
||||||
outline: 1px dashed #f38518;
|
outline: 1px dashed #f38518;
|
||||||
outline-offset: -3px;
|
outline-offset: -3px;
|
||||||
@@ -135,7 +135,7 @@ panel {
|
|||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab > .tabLabel.active {
|
.tab > .tabLabel.selected {
|
||||||
border-bottom: 1px solid;
|
border-bottom: 1px solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ tab-header .action-item {
|
|||||||
line-height: 1.4em;
|
line-height: 1.4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
tab-header .tab-header.active .action-label, /* always show it for active tab */
|
tab-header .tab-header.selected .action-label, /* always show it for selected tab */
|
||||||
tab-header .tab-header:hover .action-label, /* always show it on hover */
|
tab-header .tab-header:hover .action-label, /* always show it on hover */
|
||||||
tab-header .tab-header:focus .action-label { /* always show it on focus */
|
tab-header .tab-header:focus .action-label { /* always show it on focus */
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|||||||
@@ -57,10 +57,10 @@ let idPool = 0;
|
|||||||
<div *ngIf="_options.layout === NavigationBarLayout.vertical" class="vertical-tab-action-container">
|
<div *ngIf="_options.layout === NavigationBarLayout.vertical" class="vertical-tab-action-container">
|
||||||
<button [attr.aria-expanded]="_tabExpanded" [title]="toggleTabPanelButtonAriaLabel" [attr.aria-label]="toggleTabPanelButtonAriaLabel" [ngClass]="toggleTabPanelButtonCssClass" tabindex="0" (click)="toggleTabPanel()"></button>
|
<button [attr.aria-expanded]="_tabExpanded" [title]="toggleTabPanelButtonAriaLabel" [attr.aria-label]="toggleTabPanelButtonAriaLabel" [ngClass]="toggleTabPanelButtonCssClass" tabindex="0" (click)="toggleTabPanel()"></button>
|
||||||
</div>
|
</div>
|
||||||
<div [style.display]="_tabExpanded ? 'flex': 'none'" [attr.aria-hidden]="_tabExpanded ? 'false': 'true'" class="tabList" role="tablist" (keydown)="onKey($event)">
|
<div [style.display]="_tabExpanded ? 'flex': 'none'" [attr.aria-hidden]="_tabExpanded ? 'false': 'true'" class="tabList" role="tablist" (keydown)="onKey($event)" (focusout)="onTabHeaderFocusOut($event)">
|
||||||
<div role="presentation" *ngFor="let tab of _tabs">
|
<div role="presentation" *ngFor="let tab of _tabs">
|
||||||
<ng-container *ngIf="tab.type!=='group-header'">
|
<ng-container *ngIf="tab.type!=='group-header'">
|
||||||
<tab-header role="presentation" [active]="_activeTab === tab" [tab]="tab" [showIcon]="_options.showIcon" (onSelectTab)='selectTab($event)' (onCloseTab)='closeTab($event)'></tab-header>
|
<tab-header role="presentation" [selected]="_selectedTab === tab" [tab]="tab" [showIcon]="_options.showIcon" (onSelectTab)='selectTab($event)' (onCloseTab)='closeTab($event)'></tab-header>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="tab.type==='group-header' && _options.layout === NavigationBarLayout.vertical">
|
<ng-container *ngIf="tab.type==='group-header' && _options.layout === NavigationBarLayout.vertical">
|
||||||
<div class="tab-group-header">
|
<div class="tab-group-header">
|
||||||
@@ -102,7 +102,7 @@ export class PanelComponent extends Disposable implements IThemable {
|
|||||||
@Output() public onTabChange = new EventEmitter<TabComponent>();
|
@Output() public onTabChange = new EventEmitter<TabComponent>();
|
||||||
@Output() public onTabClose = new EventEmitter<TabComponent>();
|
@Output() public onTabClose = new EventEmitter<TabComponent>();
|
||||||
|
|
||||||
private _activeTab?: TabComponent;
|
private _selectedTab?: TabComponent;
|
||||||
private _actionbar?: ActionBar;
|
private _actionbar?: ActionBar;
|
||||||
private _mru: TabComponent[] = [];
|
private _mru: TabComponent[] = [];
|
||||||
private _tabExpanded: boolean = true;
|
private _tabExpanded: boolean = true;
|
||||||
@@ -218,39 +218,43 @@ export class PanelComponent extends Disposable implements IThemable {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this._activeTab && tab === this._activeTab) {
|
if (this._selectedTab && tab === this._selectedTab) {
|
||||||
this.onTabChange.emit(tab);
|
this.onTabChange.emit(tab);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._zone.run(() => {
|
this._zone.run(() => {
|
||||||
if (this._activeTab) {
|
if (this._selectedTab) {
|
||||||
this._activeTab.active = false;
|
this._selectedTab.selected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._activeTab = tab;
|
this._selectedTab = tab;
|
||||||
this.setMostRecentlyUsed(tab);
|
this.setMostRecentlyUsed(tab);
|
||||||
this._activeTab.active = true;
|
this._selectedTab.selected = true;
|
||||||
|
|
||||||
this.onTabChange.emit(tab);
|
this.onTabChange.emit(tab);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._tabHeaders?.forEach(tabHeader => {
|
||||||
|
tabHeader.tabIndex = tabHeader.tab.identifier === foundTab.identifier ? 0 : -1;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the id of the active tab
|
* Get the id of the selected tab
|
||||||
*/
|
*/
|
||||||
public get getActiveTab(): string | undefined {
|
public get getSelectedTab(): string | undefined {
|
||||||
return this._activeTab?.identifier;
|
return this._selectedTab?.identifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select on the next tab
|
* Select on the next tab
|
||||||
*/
|
*/
|
||||||
public selectOnNextTab(): void {
|
public selectOnNextTab(): void {
|
||||||
let activeIndex = this._tabs.toArray().findIndex(i => i === this._activeTab);
|
let selectedIndex = this._tabs.toArray().findIndex(i => i === this._selectedTab);
|
||||||
let nextTabIndex = activeIndex + 1;
|
let nextTabIndex = selectedIndex + 1;
|
||||||
if (nextTabIndex === this._tabs.length) {
|
if (nextTabIndex === this._tabs.length) {
|
||||||
nextTabIndex = 0;
|
nextTabIndex = 0;
|
||||||
}
|
}
|
||||||
@@ -315,7 +319,7 @@ export class PanelComponent extends Disposable implements IThemable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public layout() {
|
public layout() {
|
||||||
this._activeTab?.layout();
|
this._selectedTab?.layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
onKey(e: KeyboardEvent): void {
|
onKey(e: KeyboardEvent): void {
|
||||||
@@ -328,15 +332,34 @@ export class PanelComponent extends Disposable implements IThemable {
|
|||||||
this.focusPreviousTab();
|
this.focusPreviousTab();
|
||||||
eventHandled = true;
|
eventHandled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eventHandled) {
|
if (eventHandled) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onTabHeaderFocusOut(e: Event): void {
|
||||||
|
/**
|
||||||
|
* Making the selected tab header focusable when the focus leaves the tab header div.
|
||||||
|
* This fixes an issue when users press up/left arrow in vertical tab header and move up to
|
||||||
|
* the previous tab header. The next focus was being set to the selected tab and then the tab
|
||||||
|
* contents. Now, the focus will directly move to tab contents. And, when users press
|
||||||
|
* shift-tab on the first focusable element of tab content, the focus will move back to
|
||||||
|
* selected tab header.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!(<HTMLElement>e.currentTarget).contains((<any>e).relatedTarget)) {
|
||||||
|
this._tabHeaders.forEach(th => {
|
||||||
|
if (th.tab === this._selectedTab) {
|
||||||
|
th.tabIndex = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private focusPreviousTab(): void {
|
private focusPreviousTab(): void {
|
||||||
const currentIndex = this.focusedTabHeaderIndex;
|
const currentIndex = this.focusedTabHeaderIndex;
|
||||||
|
this._tabHeaders.toArray()[currentIndex].tabIndex = -1;
|
||||||
if (currentIndex !== -1) {
|
if (currentIndex !== -1) {
|
||||||
// Move to the previous tab, if we are at the first tab then move to the last tab.
|
// Move to the previous tab, if we are at the first tab then move to the last tab.
|
||||||
this.focusOnTabHeader(currentIndex === 0 ? this._tabHeaders.length - 1 : currentIndex - 1);
|
this.focusOnTabHeader(currentIndex === 0 ? this._tabHeaders.length - 1 : currentIndex - 1);
|
||||||
@@ -345,6 +368,7 @@ export class PanelComponent extends Disposable implements IThemable {
|
|||||||
|
|
||||||
private focusNextTab(): void {
|
private focusNextTab(): void {
|
||||||
const currentIndex = this.focusedTabHeaderIndex;
|
const currentIndex = this.focusedTabHeaderIndex;
|
||||||
|
this._tabHeaders.toArray()[currentIndex].tabIndex = -1;
|
||||||
if (currentIndex !== -1) {
|
if (currentIndex !== -1) {
|
||||||
// Move to the next tab, if we are at the last tab then move to the first tab.
|
// Move to the next tab, if we are at the last tab then move to the first tab.
|
||||||
this.focusOnTabHeader(currentIndex === this._tabHeaders.length - 1 ? 0 : currentIndex + 1);
|
this.focusOnTabHeader(currentIndex === this._tabHeaders.length - 1 ? 0 : currentIndex + 1);
|
||||||
@@ -366,32 +390,36 @@ export class PanelComponent extends Disposable implements IThemable {
|
|||||||
style(styles: ITabbedPanelStyles) {
|
style(styles: ITabbedPanelStyles) {
|
||||||
if (this._styleElement) {
|
if (this._styleElement) {
|
||||||
const content: string[] = [];
|
const content: string[] = [];
|
||||||
if (styles.titleInactiveForeground) {
|
if (styles.titleUnSelectedForeground) {
|
||||||
content.push(`.tabbedPanel.horizontal > .title .tabList .tab-header {
|
content.push(`.tabbedPanel.horizontal > .title .tabList .tab-header {
|
||||||
color: ${styles.titleInactiveForeground}
|
color: ${styles.titleUnSelectedForeground}
|
||||||
}`);
|
}`);
|
||||||
}
|
}
|
||||||
if (styles.titleActiveBorder && styles.titleActiveForeground) {
|
if (styles.titleSelectedBorder && styles.titleSelectedForeground) {
|
||||||
content.push(`.tabbedPanel.horizontal > .title .tabList .tab-header:focus,
|
content.push(`.tabbedPanel.horizontal > .title .tabList .tab-header.selected {
|
||||||
.tabbedPanel.horizontal > .title .tabList .tab-header.active {
|
border-color: ${styles.titleSelectedBorder};
|
||||||
border-color: ${styles.titleActiveBorder};
|
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
color: ${styles.titleActiveForeground}
|
color: ${styles.titleSelectedForeground}
|
||||||
}`);
|
}`);
|
||||||
|
|
||||||
content.push(`.tabbedPanel.horizontal > .title .tabList .tab-header:focus,
|
content.push(`.tabbedPanel.horizontal > .title .tabList .tab-header.selected {;
|
||||||
.tabbedPanel.horizontal > .title .tabList .tab-header.active {;
|
border-width: 0 0 ${styles.selectedTabContrastBorder ? '0' : '2'}px 0;
|
||||||
border-width: 0 0 ${styles.activeTabContrastBorder ? '0' : '2'}px 0;
|
|
||||||
}`);
|
}`);
|
||||||
|
|
||||||
content.push(`.tabbedPanel.horizontal > .title .tabList .tab-header:hover {
|
content.push(`.tabbedPanel.horizontal > .title .tabList .tab-header:hover {
|
||||||
color: ${styles.titleActiveForeground}
|
color: ${styles.titleSelectedForeground}
|
||||||
|
}`);
|
||||||
|
|
||||||
|
content.push(`.tabbedPanel.horizontal > .title .tabList .tab-header:focus {
|
||||||
|
outline: 1px solid;
|
||||||
|
outline-offset: 2px;
|
||||||
|
outline-color: ${styles.titleSelectedBorder};
|
||||||
}`);
|
}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (styles.activeBackgroundForVerticalLayout) {
|
if (styles.selectedBackgroundForVerticalLayout) {
|
||||||
content.push(`.tabbedPanel.vertical > .title .tabList .tab-header.active {
|
content.push(`.tabbedPanel.vertical > .title .tabList .tab-header.selected {
|
||||||
background-color:${styles.activeBackgroundForVerticalLayout}
|
background-color:${styles.selectedBackgroundForVerticalLayout}
|
||||||
}`);
|
}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,18 +435,14 @@ export class PanelComponent extends Disposable implements IThemable {
|
|||||||
}`);
|
}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (styles.activeTabContrastBorder) {
|
if (styles.selectedTabContrastBorder) {
|
||||||
content.push(`
|
content.push(`
|
||||||
.tabbedPanel > .title .tabList .tab-header.active {
|
.tabbedPanel > .title .tabList .tab-header.selected {
|
||||||
outline: 1px solid;
|
outline: 1px solid;
|
||||||
outline-offset: -3px;
|
outline-offset: -3px;
|
||||||
outline-color: ${styles.activeTabContrastBorder};
|
outline-color: ${styles.selectedTabContrastBorder};
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
} else {
|
|
||||||
content.push(`.tabbedPanel.horizontal > .title .tabList .tab-header:focus {
|
|
||||||
outline-width: 0px;
|
|
||||||
}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const newStyles = content.join('\n');
|
const newStyles = content.join('\n');
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ import { Color } from 'vs/base/common/color';
|
|||||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||||
|
|
||||||
export interface ITabbedPanelStyles {
|
export interface ITabbedPanelStyles {
|
||||||
titleActiveForeground?: Color;
|
titleSelectedForeground?: Color;
|
||||||
titleActiveBorder?: Color;
|
titleSelectedBorder?: Color;
|
||||||
titleInactiveForeground?: Color;
|
titleUnSelectedForeground?: Color;
|
||||||
focusBorder?: Color;
|
focusBorder?: Color;
|
||||||
outline?: Color;
|
outline?: Color;
|
||||||
activeBackgroundForVerticalLayout?: Color;
|
selectedBackgroundForVerticalLayout?: Color;
|
||||||
border?: Color;
|
border?: Color;
|
||||||
activeTabContrastBorder?: Color;
|
selectedTabContrastBorder?: Color;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IPanelOptions {
|
export interface IPanelOptions {
|
||||||
@@ -108,7 +108,7 @@ export class TabbedPanel extends Disposable {
|
|||||||
return this.parent;
|
return this.parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get activeTabId(): string | undefined {
|
public get selectedTabId(): string | undefined {
|
||||||
return this._shownTabId;
|
return this._shownTabId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,8 +209,8 @@ export class TabbedPanel extends Disposable {
|
|||||||
if (this._shownTabId) {
|
if (this._shownTabId) {
|
||||||
const shownTab = this._tabMap.get(this._shownTabId);
|
const shownTab = this._tabMap.get(this._shownTabId);
|
||||||
if (shownTab) {
|
if (shownTab) {
|
||||||
shownTab.label.classList.remove('active');
|
shownTab.label.classList.remove('selected');
|
||||||
shownTab.header.classList.remove('active');
|
shownTab.header.classList.remove('selected');
|
||||||
shownTab.header.setAttribute('aria-selected', 'false');
|
shownTab.header.setAttribute('aria-selected', 'false');
|
||||||
shownTab.header.tabIndex = -1;
|
shownTab.header.tabIndex = -1;
|
||||||
if (shownTab.body) {
|
if (shownTab.body) {
|
||||||
@@ -239,8 +239,8 @@ export class TabbedPanel extends Disposable {
|
|||||||
}
|
}
|
||||||
this.body.appendChild(tab.body);
|
this.body.appendChild(tab.body);
|
||||||
this.body.setAttribute('aria-labelledby', tab.tab.identifier);
|
this.body.setAttribute('aria-labelledby', tab.tab.identifier);
|
||||||
tab.label.classList.add('active');
|
tab.label.classList.add('selected');
|
||||||
tab.header.classList.add('active');
|
tab.header.classList.add('selected');
|
||||||
tab.header.setAttribute('aria-selected', 'true');
|
tab.header.setAttribute('aria-selected', 'true');
|
||||||
this._onTabChange.fire(id);
|
this._onTabChange.fire(id);
|
||||||
if (tab.tab.view.onShow) {
|
if (tab.tab.view.onShow) {
|
||||||
@@ -329,27 +329,27 @@ export class TabbedPanel extends Disposable {
|
|||||||
}`);
|
}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (styles.titleActiveForeground && styles.titleActiveBorder) {
|
if (styles.titleSelectedForeground && styles.titleSelectedBorder) {
|
||||||
content.push(`
|
content.push(`
|
||||||
.tabbedPanel > .title .tabList .tab:hover .tabLabel,
|
.tabbedPanel > .title .tabList .tab:hover .tabLabel,
|
||||||
.tabbedPanel > .title .tabList .tab .tabLabel.active {
|
.tabbedPanel > .title .tabList .tab .tabLabel.selected {
|
||||||
color: ${styles.titleActiveForeground};
|
color: ${styles.titleSelectedForeground};
|
||||||
border-bottom-color: ${styles.titleActiveBorder};
|
border-bottom-color: ${styles.titleSelectedBorder};
|
||||||
border-bottom-width: 2px;
|
border-bottom-width: 2px;
|
||||||
}`);
|
}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (styles.titleInactiveForeground) {
|
if (styles.titleUnSelectedForeground) {
|
||||||
content.push(`
|
content.push(`
|
||||||
.tabbedPanel > .title .tabList .tab .tabLabel {
|
.tabbedPanel > .title .tabList .tab .tabLabel {
|
||||||
color: ${styles.titleInactiveForeground};
|
color: ${styles.titleUnSelectedForeground};
|
||||||
}`);
|
}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (styles.focusBorder && styles.titleActiveForeground) {
|
if (styles.focusBorder && styles.titleSelectedForeground) {
|
||||||
content.push(`
|
content.push(`
|
||||||
.tabbedPanel > .title .tabList .tab .tabLabel:focus {
|
.tabbedPanel > .title .tabList .tab .tabLabel:focus {
|
||||||
color: ${styles.titleActiveForeground};
|
color: ${styles.titleSelectedForeground};
|
||||||
border-bottom-color: ${styles.focusBorder} !important;
|
border-bottom-color: ${styles.focusBorder} !important;
|
||||||
border-bottom: 1px solid;
|
border-bottom: 1px solid;
|
||||||
outline: none;
|
outline: none;
|
||||||
@@ -358,7 +358,7 @@ export class TabbedPanel extends Disposable {
|
|||||||
|
|
||||||
if (styles.outline) {
|
if (styles.outline) {
|
||||||
content.push(`
|
content.push(`
|
||||||
.tabbedPanel > .title .tabList .tab-header.active,
|
.tabbedPanel > .title .tabList .tab-header.selected,
|
||||||
.tabbedPanel > .title .tabList .tab-header:hover {
|
.tabbedPanel > .title .tabList .tab-header:hover {
|
||||||
outline-color: ${styles.outline};
|
outline-color: ${styles.outline};
|
||||||
outline-width: 1px;
|
outline-width: 1px;
|
||||||
@@ -367,7 +367,7 @@ export class TabbedPanel extends Disposable {
|
|||||||
outline-offset: -5px;
|
outline-offset: -5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabbedPanel > .title .tabList .tab-header:hover:not(.active) {
|
.tabbedPanel > .title .tabList .tab-header:hover:not(.selected) {
|
||||||
outline-style: dashed;
|
outline-style: dashed;
|
||||||
}`);
|
}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export class TabComponent implements OnDestroy {
|
|||||||
@Input() public canClose!: boolean;
|
@Input() public canClose!: boolean;
|
||||||
@Input() public actions?: Array<Action>;
|
@Input() public actions?: Array<Action>;
|
||||||
@Input() public iconClass?: string;
|
@Input() public iconClass?: string;
|
||||||
public _active = false;
|
private _selected = false;
|
||||||
@Input() public identifier!: string;
|
@Input() public identifier!: string;
|
||||||
@Input() public type: TabType = 'tab';
|
@Input() public type: TabType = 'tab';
|
||||||
@Input() private visibilityType: 'if' | 'visibility' = 'if';
|
@Input() private visibilityType: 'if' | 'visibility' = 'if';
|
||||||
@@ -38,7 +38,7 @@ export class TabComponent implements OnDestroy {
|
|||||||
|
|
||||||
@ContentChild(TabChild) public set child(tab: TabChild) {
|
@ContentChild(TabChild) public set child(tab: TabChild) {
|
||||||
this._child = tab;
|
this._child = tab;
|
||||||
if (this.active && this._child) {
|
if (this.selected && this._child) {
|
||||||
this._child.layout();
|
this._child.layout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,21 +47,21 @@ export class TabComponent implements OnDestroy {
|
|||||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef
|
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
public set active(val: boolean) {
|
public set selected(val: boolean) {
|
||||||
if (!this.destroyed) {
|
if (!this.destroyed) {
|
||||||
this._active = val;
|
this._selected = val;
|
||||||
if (this.active) {
|
if (this.selected) {
|
||||||
this.rendered = true;
|
this.rendered = true;
|
||||||
}
|
}
|
||||||
this._cd.detectChanges();
|
this._cd.detectChanges();
|
||||||
if (this.active && this._child) {
|
if (this.selected && this._child) {
|
||||||
this._child.layout();
|
this._child.layout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public get active(): boolean {
|
public get selected(): boolean {
|
||||||
return this._active;
|
return this._selected;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
@@ -72,7 +72,7 @@ export class TabComponent implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
shouldBeIfed(): boolean {
|
shouldBeIfed(): boolean {
|
||||||
if (this.active) {
|
if (this.selected) {
|
||||||
return true;
|
return true;
|
||||||
} else if (this.visibilityType === 'visibility' && this.rendered) {
|
} else if (this.visibilityType === 'visibility' && this.rendered) {
|
||||||
return true;
|
return true;
|
||||||
@@ -82,7 +82,7 @@ export class TabComponent implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
shouldBeHidden(): boolean {
|
shouldBeHidden(): boolean {
|
||||||
if (this.visibilityType === 'visibility' && !this.active) {
|
if (this.visibilityType === 'visibility' && !this.selected) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -19,10 +19,10 @@ import { CloseTabAction } from 'sql/base/browser/ui/panel/tabActions';
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'tab-header',
|
selector: 'tab-header',
|
||||||
template: `
|
template: `
|
||||||
<div #actionHeader role="tab" [attr.aria-selected]="tab.active" [attr.aria-label]="tab.title" class="tab-header" style="flex: 0 0; flex-direction: row;" [class.active]="tab.active" tabindex="0" (click)="selectTab(tab)" (keyup)="onKey($event)">
|
<div #actionHeader role="tab" [attr.aria-selected]="tab.selected" [attr.aria-label]="tab.title" class="tab-header" style="flex: 0 0; flex-direction: row;" [class.selected]="tab.selected" [attr.tabindex] = "_tabIndex" (click)="selectTab(tab)" (keyup)="onKey($event)">
|
||||||
<div class="tab" role="presentation">
|
<div class="tab" role="presentation">
|
||||||
<a #tabIcon *ngIf="showIcon && tab.iconClass" class="tabIcon codicon icon {{tab.iconClass}}"></a>
|
<a #tabIcon *ngIf="showIcon && tab.iconClass" class="tabIcon codicon icon {{tab.iconClass}}"></a>
|
||||||
<a class="tabLabel" [class.active]="tab.active" [title]="tab.title" #tabLabel>{{tab.title}}</a>
|
<a class="tabLabel" [class.selected]="tab.selected" [title]="tab.title" #tabLabel>{{tab.title}}</a>
|
||||||
</div>
|
</div>
|
||||||
<div #actionbar style="flex: 0 0 auto; align-self: end; margin-top: auto; margin-bottom: auto;" ></div>
|
<div #actionbar style="flex: 0 0 auto; align-self: end; margin-top: auto; margin-bottom: auto;" ></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -31,12 +31,13 @@ import { CloseTabAction } from 'sql/base/browser/ui/panel/tabActions';
|
|||||||
export class TabHeaderComponent extends Disposable implements AfterContentInit, OnDestroy {
|
export class TabHeaderComponent extends Disposable implements AfterContentInit, OnDestroy {
|
||||||
@Input() public tab!: TabComponent;
|
@Input() public tab!: TabComponent;
|
||||||
@Input() public showIcon?: boolean;
|
@Input() public showIcon?: boolean;
|
||||||
@Input() public active?: boolean;
|
@Input() public selected?: boolean;
|
||||||
@Output() public onSelectTab: EventEmitter<TabComponent> = new EventEmitter<TabComponent>();
|
@Output() public onSelectTab: EventEmitter<TabComponent> = new EventEmitter<TabComponent>();
|
||||||
@Output() public onCloseTab: EventEmitter<TabComponent> = new EventEmitter<TabComponent>();
|
@Output() public onCloseTab: EventEmitter<TabComponent> = new EventEmitter<TabComponent>();
|
||||||
@Output() public onFocusTab: EventEmitter<TabComponent> = new EventEmitter<TabComponent>();
|
@Output() public onFocusTab: EventEmitter<TabComponent> = new EventEmitter<TabComponent>();
|
||||||
|
|
||||||
private _actionbar!: ActionBar;
|
private _actionbar!: ActionBar;
|
||||||
|
private _tabIndex: number = -1;
|
||||||
|
|
||||||
@ViewChild('actionHeader', { read: ElementRef }) private _actionHeaderRef!: ElementRef;
|
@ViewChild('actionHeader', { read: ElementRef }) private _actionHeaderRef!: ElementRef;
|
||||||
@ViewChild('actionbar', { read: ElementRef }) private _actionbarRef!: ElementRef;
|
@ViewChild('actionbar', { read: ElementRef }) private _actionbarRef!: ElementRef;
|
||||||
@@ -47,6 +48,15 @@ export class TabHeaderComponent extends Disposable implements AfterContentInit,
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get tabIndex(): number {
|
||||||
|
return this._tabIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set tabIndex(value: number) {
|
||||||
|
this._tabIndex = value;
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
public get nativeElement(): HTMLElement {
|
public get nativeElement(): HTMLElement {
|
||||||
return this._actionHeaderRef.nativeElement;
|
return this._actionHeaderRef.nativeElement;
|
||||||
}
|
}
|
||||||
@@ -66,6 +76,9 @@ export class TabHeaderComponent extends Disposable implements AfterContentInit,
|
|||||||
this._actionbar.push(closeAction, { icon: true, label: false });
|
this._actionbar.push(closeAction, { icon: true, label: false });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (this.tab.selected) {
|
||||||
|
this.tabIndex = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
|
|||||||
@@ -1118,8 +1118,8 @@ export class Designer extends Disposable implements IThemable {
|
|||||||
private saveUIState(): void {
|
private saveUIState(): void {
|
||||||
if (this._input) {
|
if (this._input) {
|
||||||
this._input.designerUIState = {
|
this._input.designerUIState = {
|
||||||
activeContentTabId: this._contentTabbedPanel.activeTabId,
|
activeContentTabId: this._contentTabbedPanel.selectedTabId,
|
||||||
activeScriptTabId: this._scriptTabbedPannel.activeTabId
|
activeScriptTabId: this._scriptTabbedPannel.selectedTabId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,14 +45,14 @@ export function attachPanelStyler(widget: IThemable, themeService: IThemeService
|
|||||||
|
|
||||||
export function attachTabbedPanelStyler(widget: IThemable, themeService: IThemeService) {
|
export function attachTabbedPanelStyler(widget: IThemable, themeService: IThemeService) {
|
||||||
return attachStyler(themeService, {
|
return attachStyler(themeService, {
|
||||||
titleActiveForeground: PANEL_ACTIVE_TITLE_FOREGROUND,
|
titleSelectedForeground: PANEL_ACTIVE_TITLE_FOREGROUND,
|
||||||
titleActiveBorder: PANEL_ACTIVE_TITLE_BORDER,
|
titleSelectedBorder: PANEL_ACTIVE_TITLE_BORDER,
|
||||||
titleInactiveForeground: PANEL_INACTIVE_TITLE_FOREGROUND,
|
titleUnSelectedForeground: PANEL_INACTIVE_TITLE_FOREGROUND,
|
||||||
focusBorder: cr.focusBorder,
|
focusBorder: cr.focusBorder,
|
||||||
outline: cr.activeContrastBorder,
|
outline: cr.activeContrastBorder,
|
||||||
activeBackgroundForVerticalLayout: VERTICAL_TAB_ACTIVE_BACKGROUND,
|
selectedBackgroundForVerticalLayout: VERTICAL_TAB_ACTIVE_BACKGROUND,
|
||||||
border: DASHBOARD_BORDER,
|
border: DASHBOARD_BORDER,
|
||||||
activeTabContrastBorder: cr.activeContrastBorder
|
selectedTabContrastBorder: cr.activeContrastBorder
|
||||||
}, widget);
|
}, widget);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -149,8 +149,8 @@ export class DashboardNavSection extends DashboardTab implements OnDestroy, OnCh
|
|||||||
|
|
||||||
public layout() {
|
public layout() {
|
||||||
if (this._tabs) {
|
if (this._tabs) {
|
||||||
const activeTabId = this._panel.getActiveTab;
|
const selectedTabId = this._panel.getSelectedTab;
|
||||||
const localtab = this._tabs.find(i => i.id === activeTabId);
|
const localtab = this._tabs.find(i => i.id === selectedTabId);
|
||||||
this._cd.detectChanges();
|
this._cd.detectChanges();
|
||||||
localtab.layout();
|
localtab.layout();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user