mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-22 17:22:59 -05:00
fix keyboard focus issues (#16206)
This commit is contained in:
@@ -281,10 +281,6 @@ export class ConnectionBrowserView extends Disposable implements IPanelView {
|
||||
this.treeContainer.style.height = `${treeHeight}px`;
|
||||
this.tree.layout(treeHeight, dimension.width);
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
this.filterInput.focus();
|
||||
}
|
||||
}
|
||||
|
||||
export interface ITreeItemFromProvider {
|
||||
|
||||
@@ -191,9 +191,6 @@ export class ConnectionDialogWidget extends Modal {
|
||||
},
|
||||
layout: (dimension: DOM.Dimension) => {
|
||||
this._recentConnectionTree.layout(dimension.height - DOM.getTotalHeight(this._recentConnectionActionBarContainer));
|
||||
},
|
||||
focus: () => {
|
||||
this._actionbar.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ import { DialogPane } from 'sql/workbench/services/dialog/browser/dialogPane';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IBootstrapParams } from 'sql/workbench/services/bootstrap/common/bootstrapParams';
|
||||
import { ComponentEventType } from 'sql/platform/dashboard/browser/interfaces';
|
||||
import { getFocusableElements } from 'sql/base/browser/dom';
|
||||
|
||||
export interface LayoutRequestParams {
|
||||
modelViewId?: string;
|
||||
@@ -20,6 +21,7 @@ export interface DialogComponentParams extends IBootstrapParams {
|
||||
validityChangedCallback: (valid: boolean) => void;
|
||||
onLayoutRequested: Event<LayoutRequestParams>;
|
||||
dialogPane: DialogPane;
|
||||
setInitialFocus: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@@ -62,12 +64,20 @@ export class DialogContainer implements AfterViewInit {
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
const element = <HTMLElement>this._el.nativeElement;
|
||||
this._modelViewContent.onEvent(event => {
|
||||
if (event.isRootComponent && event.eventType === ComponentEventType.validityChanged) {
|
||||
this._params.validityChangedCallback(event.args);
|
||||
}
|
||||
|
||||
// Set focus to the first focusable elements when the content is loaded and the focus is not currently inside the content area
|
||||
if (this._params.setInitialFocus && event.isRootComponent && event.eventType === ComponentEventType.onComponentLoaded && !element.contains(document.activeElement)) {
|
||||
const focusableElements = getFocusableElements(element);
|
||||
if (focusableElements?.length > 0) {
|
||||
focusableElements[0].focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
let element = <HTMLElement>this._el.nativeElement;
|
||||
element.style.height = '100%';
|
||||
element.style.width = '100%';
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IThemable } from 'vs/base/common/styler';
|
||||
import { attachTabbedPanelStyler } from 'sql/workbench/common/styler';
|
||||
import { localize } from 'vs/nls';
|
||||
import { getFocusableElements } from 'sql/base/browser/dom';
|
||||
|
||||
export class DialogPane extends Disposable implements IThemable {
|
||||
private _tabbedPanel: TabbedPanel | undefined;
|
||||
@@ -44,6 +45,7 @@ export class DialogPane extends Disposable implements IThemable {
|
||||
private _themeService: IThemeService,
|
||||
public displayPageTitle: boolean,
|
||||
public description?: string,
|
||||
private setInitialFocus: boolean = true
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@@ -56,7 +58,7 @@ export class DialogPane extends Disposable implements IThemable {
|
||||
this._body = DOM.append(container, DOM.$('div.dialogModal-pane'));
|
||||
if (typeof this._content === 'string' || this._content.length < 2) {
|
||||
let modelViewId = typeof this._content === 'string' ? this._content : this._content[0].content;
|
||||
this.initializeModelViewContainer(this._body, modelViewId);
|
||||
this.initializeModelViewContainer(this._body, modelViewId, undefined, this.setInitialFocus);
|
||||
} else {
|
||||
this._tabbedPanel = new TabbedPanel(this._body);
|
||||
attachTabbedPanelStyler(this._tabbedPanel, this._themeService);
|
||||
@@ -67,7 +69,8 @@ export class DialogPane extends Disposable implements IThemable {
|
||||
let tabContainer = document.createElement('div');
|
||||
tabContainer.style.display = 'none';
|
||||
this._body.appendChild(tabContainer);
|
||||
this.initializeModelViewContainer(tabContainer, tab.content, tab);
|
||||
// Only set initial focus when the tab is active one.
|
||||
this.initializeModelViewContainer(tabContainer, tab.content, tab, this.setInitialFocus && tabIndex === this._selectedTabIndex);
|
||||
this._tabbedPanel!.onTabChange(e => {
|
||||
tabContainer.style.height = (this.getTabDimension().height - this._tabbedPanel!.headersize) + 'px';
|
||||
this._onLayoutChange.fire({ modelViewId: tab.content });
|
||||
@@ -83,8 +86,7 @@ export class DialogPane extends Disposable implements IThemable {
|
||||
container.appendChild(tabContainer);
|
||||
tabContainer.style.display = 'block';
|
||||
},
|
||||
layout: (dimension) => { this.getTabDimension(); },
|
||||
focus: () => { this.focus(); }
|
||||
layout: (dimension) => { this.getTabDimension(); }
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -113,7 +115,7 @@ export class DialogPane extends Disposable implements IThemable {
|
||||
/**
|
||||
* Bootstrap angular for the dialog's model view controller with the given model view ID
|
||||
*/
|
||||
private initializeModelViewContainer(bodyContainer: HTMLElement, modelViewId: string, tab?: DialogTab) {
|
||||
private initializeModelViewContainer(bodyContainer: HTMLElement, modelViewId: string, tab?: DialogTab, setInitialFocus: boolean = true) {
|
||||
this._instantiationService.invokeFunction<void, any[]>(bootstrapAngular,
|
||||
DialogModule,
|
||||
bodyContainer,
|
||||
@@ -127,7 +129,8 @@ export class DialogPane extends Disposable implements IThemable {
|
||||
}
|
||||
},
|
||||
onLayoutRequested: this._onLayoutChange.event,
|
||||
dialogPane: this
|
||||
dialogPane: this,
|
||||
setInitialFocus: setInitialFocus
|
||||
} as DialogComponentParams,
|
||||
undefined,
|
||||
(moduleRef: NgModuleRef<{}>) => {
|
||||
@@ -147,8 +150,10 @@ export class DialogPane extends Disposable implements IThemable {
|
||||
}
|
||||
|
||||
private focus(): void {
|
||||
let focusedElement = <HTMLElement>this._body.querySelector('a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled])');
|
||||
focusedElement ? focusedElement.focus() : this._body.focus();
|
||||
const focusableElements = getFocusableElements(this._body);
|
||||
if (focusableElements?.length > 0) {
|
||||
focusableElements[0].focus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -133,8 +133,8 @@ export class WizardModal extends Modal {
|
||||
this._mpContainer = append(this._body, $('div.dialog-message-and-page-container'));
|
||||
this._pageContainer = append(this._mpContainer, $('div.dialogModal-page-container'));
|
||||
|
||||
this._wizard.pages.forEach(page => {
|
||||
this.registerPage(page);
|
||||
this._wizard.pages.forEach((page, index) => {
|
||||
this.registerPage(page, index === 0); // only do auto-focus for the first page.
|
||||
});
|
||||
this._wizard.onPageAdded(page => {
|
||||
this.registerPage(page);
|
||||
@@ -167,8 +167,8 @@ export class WizardModal extends Modal {
|
||||
});
|
||||
}
|
||||
|
||||
private registerPage(page: WizardPage): void {
|
||||
let dialogPane = new DialogPane(page.title, page.content, valid => page.notifyValidityChanged(valid), this._instantiationService, this._themeService, this._wizard.displayPageTitles, page.description);
|
||||
private registerPage(page: WizardPage, setInitialFocus: boolean = false): void {
|
||||
let dialogPane = new DialogPane(page.title, page.content, valid => page.notifyValidityChanged(valid), this._instantiationService, this._themeService, this._wizard.displayPageTitles, page.description, setInitialFocus);
|
||||
dialogPane.createBody(this._pageContainer);
|
||||
this._dialogPanes.set(page, dialogPane);
|
||||
page.onUpdate(() => this.setButtonsForPage(this._wizard.currentPage));
|
||||
|
||||
@@ -334,8 +334,7 @@ export class RestoreDialog extends Modal {
|
||||
render: c => {
|
||||
DOM.append(c, generalTab);
|
||||
},
|
||||
layout: () => { },
|
||||
focus: () => this._restoreFromSelectBox ? this._restoreFromSelectBox.focus() : generalTab.focus()
|
||||
layout: () => { }
|
||||
}
|
||||
});
|
||||
|
||||
@@ -346,8 +345,7 @@ export class RestoreDialog extends Modal {
|
||||
layout: () => { },
|
||||
render: c => {
|
||||
c.appendChild(fileContentElement);
|
||||
},
|
||||
focus: () => this._optionsMap[this._relocateDatabaseFilesOption] ? this._optionsMap[this._relocateDatabaseFilesOption].focus() : fileContentElement.focus()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -358,8 +356,7 @@ export class RestoreDialog extends Modal {
|
||||
layout: () => { },
|
||||
render: c => {
|
||||
c.appendChild(optionsContentElement);
|
||||
},
|
||||
focus: () => this._optionsMap[this._withReplaceDatabaseOption] ? this._optionsMap[this._withReplaceDatabaseOption].focus() : optionsContentElement.focus()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user