提交 33a0015c 编写于 作者: B Benjamin Pasero

notifications - keyboard navigation in buttons

上级 ce13bb0d
......@@ -13,6 +13,7 @@ import { KeyCode } from 'vs/base/common/keyCodes';
import { Color } from 'vs/base/common/color';
import { mixin } from 'vs/base/common/objects';
import Event, { Emitter } from 'vs/base/common/event';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
export interface IButtonOptions extends IButtonStyles {
}
......@@ -61,7 +62,7 @@ export class Button {
'role': 'button'
}).appendTo(container);
this.$el.on(DOM.EventType.CLICK, (e) => {
this.$el.on(DOM.EventType.CLICK, e => {
if (!this.enabled) {
DOM.EventHelper.stop(e);
return;
......@@ -70,7 +71,7 @@ export class Button {
this._onDidClick.fire(e);
});
this.$el.on(DOM.EventType.KEY_DOWN, (e) => {
this.$el.on(DOM.EventType.KEY_DOWN, e => {
let event = new StandardKeyboardEvent(e as KeyboardEvent);
let eventHandled = false;
if (this.enabled && event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {
......@@ -86,13 +87,13 @@ export class Button {
}
});
this.$el.on(DOM.EventType.MOUSE_OVER, (e) => {
this.$el.on(DOM.EventType.MOUSE_OVER, e => {
if (!this.$el.hasClass('disabled')) {
this.setHoverBackground();
}
});
this.$el.on(DOM.EventType.MOUSE_OUT, (e) => {
this.$el.on(DOM.EventType.MOUSE_OUT, e => {
this.applyStyles(); // restore standard styles
});
......@@ -135,7 +136,7 @@ export class Button {
}
}
getElement(): HTMLElement {
get element(): HTMLElement {
return this.$el.getHTMLElement();
}
......@@ -183,4 +184,59 @@ export class Button {
this._onDidClick.dispose();
}
}
export class ButtonGroup {
private _buttons: Button[];
private toDispose: IDisposable[];
constructor(container: Builder, count: number, options?: IButtonOptions);
constructor(container: HTMLElement, count: number, options?: IButtonOptions);
constructor(container: any, count: number, options?: IButtonOptions) {
this._buttons = [];
this.toDispose = [];
this.create(container, count, options);
}
get buttons(): Button[] {
return this._buttons;
}
private create(container: Builder, count: number, options?: IButtonOptions): void;
private create(container: HTMLElement, count: number, options?: IButtonOptions): void;
private create(container: any, count: number, options?: IButtonOptions): void {
for (let index = 0; index < count; index++) {
const button = new Button(container, options);
this._buttons.push(button);
this.toDispose.push(button);
// Implement keyboard access in buttons if there are multiple
if (count > 1) {
$(button.element).on(DOM.EventType.KEY_DOWN, e => {
const event = new StandardKeyboardEvent(e as KeyboardEvent);
let eventHandled = true;
// Next / Previous Button
let buttonIndexToFocus: number;
if (event.equals(KeyCode.LeftArrow)) {
buttonIndexToFocus = index > 0 ? index - 1 : this._buttons.length - 1;
} else if (event.equals(KeyCode.RightArrow)) {
buttonIndexToFocus = index === this._buttons.length - 1 ? 0 : index + 1;
} else {
eventHandled = false;
}
if (eventHandled) {
this._buttons[buttonIndexToFocus].focus();
DOM.EventHelper.stop(e, true);
}
}, this.toDispose);
}
}
}
dispose(): void {
this.toDispose = dispose(this.toDispose);
}
}
\ No newline at end of file
......@@ -36,12 +36,12 @@ interface INotificationToast {
export class NotificationsToasts extends Themable {
private static MAX_WIDTH = 450;
private static MAX_NOTIFICATIONS = 4;
private static MAX_NOTIFICATIONS = 3;
private static PURGE_TIMEOUT: { [severity: number]: number } = (() => {
const intervals = Object.create(null);
intervals[Severity.Info] = 8000;
intervals[Severity.Warning] = 12000;
intervals[Severity.Info] = 5000;
intervals[Severity.Warning] = 10000;
intervals[Severity.Error] = 15000;
return intervals;
......
......@@ -12,7 +12,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener';
import URI from 'vs/base/common/uri';
import { onUnexpectedError } from 'vs/base/common/errors';
import { localize } from 'vs/nls';
import { Button } from 'vs/base/browser/ui/button/button';
import { ButtonGroup } from 'vs/base/browser/ui/button/button';
import { attachButtonStyler, attachProgressBarStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IMarkdownString } from 'vs/base/common/htmlContent';
......@@ -396,7 +396,24 @@ export class NotificationTemplateRenderer {
clearNode(this.template.buttonsContainer);
if (notification.expanded) {
notification.actions.primary.forEach(action => this.createButton(notification, action));
const buttonGroup = new ButtonGroup(this.template.buttonsContainer, notification.actions.primary.length);
buttonGroup.buttons.forEach((button, index) => {
const action = notification.actions.primary[index];
button.label = action.label;
this.inputDisposeables.push(button.onDidClick(() => {
// Run action
this.actionRunner.run(action, notification);
// Hide notification
notification.dispose();
}));
this.inputDisposeables.push(attachButtonStyler(button, this.themeService));
});
this.inputDisposeables.push(buttonGroup);
}
}
......@@ -449,24 +466,6 @@ export class NotificationTemplateRenderer {
return keybinding ? keybinding.getLabel() : void 0;
}
private createButton(notification: INotificationViewItem, action: IAction): Button {
const button = new Button(this.template.buttonsContainer);
button.label = action.label;
this.inputDisposeables.push(button.onDidClick(() => {
// Run action
this.actionRunner.run(action, notification);
// Hide notification
notification.dispose();
}));
this.inputDisposeables.push(attachButtonStyler(button, this.themeService));
this.inputDisposeables.push(button);
return button;
}
public dispose(): void {
this.inputDisposeables = dispose(this.inputDisposeables);
}
......
......@@ -102,7 +102,7 @@ export class EmptyView extends ViewsViewletPanel {
public focusBody(): void {
if (this.button) {
this.button.getElement().focus();
this.button.element.focus();
}
}
......
......@@ -225,7 +225,7 @@ export class SearchWidget extends Widget {
this.toggleReplaceButton.icon = 'toggle-replace-button collapse';
// TODO@joh need to dispose this listener eventually
this.toggleReplaceButton.onDidClick(() => this.onToggleReplaceButton());
this.toggleReplaceButton.getElement().title = nls.localize('search.replace.toggle.button.title', "Toggle Replace");
this.toggleReplaceButton.element.title = nls.localize('search.replace.toggle.button.title', "Toggle Replace");
}
private renderSearchInput(parent: HTMLElement, options: ISearchWidgetOptions): void {
......@@ -301,8 +301,8 @@ export class SearchWidget extends Widget {
private onToggleReplaceButton(): void {
dom.toggleClass(this.replaceContainer, 'disabled');
dom.toggleClass(this.toggleReplaceButton.getElement(), 'collapse');
dom.toggleClass(this.toggleReplaceButton.getElement(), 'expand');
dom.toggleClass(this.toggleReplaceButton.element, 'collapse');
dom.toggleClass(this.toggleReplaceButton.element, 'expand');
this.updateReplaceActiveState();
this._onReplaceToggled.fire();
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册