coveo-search-ui
Version:
Coveo JavaScript Search Framework
267 lines (226 loc) • 6.77 kB
text/typescript
import { Logger } from '../misc/Logger';
import { ComponentEvents } from '../ui/Base/Component';
import { KEYBOARD, KeyboardUtils } from '../utils/KeyboardUtils';
import { $$, Dom } from './Dom';
import 'styling/_AccessibleButton';
export enum ArrowDirection {
UP,
RIGHT,
DOWN,
LEFT
}
export class AccessibleButton {
private element: Dom;
private labelOrTitleRequired = true;
private label: string;
private title: string;
private role: string;
private clickAction: (e: Event) => void;
private enterKeyboardAction: (e: Event) => void;
private blurAction: (e: Event) => void;
private mouseleaveAction: (e: Event) => void;
private focusAction: (e: Event) => void;
private mouseenterAction: (e: Event) => void;
private arrowsAction: (direction: ArrowDirection, e: Event) => void;
private logger: Logger;
private eventOwner: ComponentEvents;
public constructor() {
this.logger = new Logger(this);
}
public withOwner(owner: ComponentEvents) {
this.eventOwner = owner;
return this;
}
public withElement(element: Dom | HTMLElement) {
if (element instanceof HTMLElement) {
this.element = $$(element);
} else {
this.element = element;
}
return this;
}
public withLabel(label: string) {
this.label = label;
return this;
}
public withoutLabelOrTitle() {
this.labelOrTitleRequired = false;
return this;
}
public withTitle(title: string) {
this.title = title;
return this;
}
public withSelectAction(action: (e: Event) => void) {
this.clickAction = action;
this.enterKeyboardAction = action;
return this;
}
public withClickAction(clickAction: (e: Event) => void) {
this.clickAction = clickAction;
return this;
}
public withEnterKeyboardAction(enterAction: (e: Event) => void) {
this.enterKeyboardAction = enterAction;
return this;
}
public withFocusAndMouseEnterAction(action: (e: Event) => void) {
this.focusAction = action;
this.mouseenterAction = action;
return this;
}
public withFocusAction(action: (e: Event) => void) {
this.focusAction = action;
return this;
}
public withMouseEnterAction(action: (e: Event) => void) {
this.mouseenterAction = action;
return this;
}
public withBlurAndMouseLeaveAction(action: (e: Event) => void) {
this.mouseleaveAction = action;
this.blurAction = action;
return this;
}
public withMouseLeaveAction(action: (e: Event) => void) {
this.mouseleaveAction = action;
return this;
}
public withBlurAction(action: (e: Event) => void) {
this.blurAction = action;
return this;
}
public withArrowsAction(action: (direction: ArrowDirection, e: Event) => void) {
this.arrowsAction = action;
return this;
}
public withRole(role: string) {
this.role = role;
return this;
}
public build() {
if (!this.element) {
this.element = $$('div');
}
this.ensureCorrectRole();
this.ensureCorrectLabelOrTitle();
this.ensureSelectAction();
this.ensureUnselectAction();
this.ensureMouseenterAndFocusAction();
this.ensureMouseleaveAndBlurAction();
this.ensureArrowsAction();
this.ensureDifferentiationBetweenKeyboardAndMouseFocus();
return this;
}
private ensureDifferentiationBetweenKeyboardAndMouseFocus() {
const classWhenPressed = 'coveo-accessible-button-pressed';
$$(this.element).addClass('coveo-accessible-button');
$$(this.element).on('mouseup', () => $$(this.element).addClass(classWhenPressed));
$$(this.element).on('focus', () => {
$$(this.element).removeClass(classWhenPressed);
});
}
private ensureCorrectRole() {
if (!this.element.getAttribute('role')) {
this.element.setAttribute('role', this.role || 'button');
}
}
private ensureCorrectLabelOrTitle() {
if (!this.labelOrTitleRequired) {
return;
}
if (!this.label && !this.title) {
this.logger.error(`Missing a label or title to create an accessible button!`);
return;
}
if (this.title) {
this.element.setAttribute('title', this.title);
return;
}
this.element.setAttribute('aria-label', this.label);
}
private ensureTabIndex() {
this.element.setAttribute('tabindex', '0');
}
private ensureSelectAction() {
if (this.enterKeyboardAction) {
this.ensureTabIndex();
this.bindEvent(
'keyup',
KeyboardUtils.keypressAction(KEYBOARD.ENTER, (e: Event) => this.enterKeyboardAction(e))
);
this.bindEvent(
'keydown',
KeyboardUtils.keypressAction(KEYBOARD.SPACEBAR, (e: Event) => {
if (e.target instanceof HTMLInputElement) {
return;
}
e.preventDefault();
})
);
this.bindEvent(
'keyup',
KeyboardUtils.keypressAction(KEYBOARD.SPACEBAR, (e: Event) => {
if (e.target instanceof HTMLInputElement) {
return;
}
this.enterKeyboardAction(e);
})
);
}
if (this.clickAction) {
this.bindEvent('click', this.clickAction);
}
}
private ensureUnselectAction() {
if (this.blurAction) {
this.bindEvent('blur', this.blurAction);
}
if (this.mouseleaveAction) {
this.bindEvent('mouseleave', this.mouseleaveAction);
}
}
private ensureMouseenterAndFocusAction() {
if (this.mouseenterAction) {
this.bindEvent('mouseenter', this.mouseenterAction);
}
if (this.focusAction) {
this.bindEvent('focus', this.focusAction);
}
}
private ensureMouseleaveAndBlurAction() {
if (this.mouseleaveAction) {
this.bindEvent('mouseleave', this.mouseleaveAction);
}
if (this.blurAction) {
this.bindEvent('blur', this.blurAction);
}
}
private ensureArrowsAction() {
if (this.arrowsAction) {
this.bindEvent(
'keyup',
KeyboardUtils.keypressAction(KEYBOARD.UP_ARROW, (e: Event) => this.arrowsAction(ArrowDirection.UP, e))
);
this.bindEvent(
'keyup',
KeyboardUtils.keypressAction(KEYBOARD.RIGHT_ARROW, (e: Event) => this.arrowsAction(ArrowDirection.RIGHT, e))
);
this.bindEvent(
'keyup',
KeyboardUtils.keypressAction(KEYBOARD.DOWN_ARROW, (e: Event) => this.arrowsAction(ArrowDirection.DOWN, e))
);
this.bindEvent(
'keyup',
KeyboardUtils.keypressAction(KEYBOARD.LEFT_ARROW, (e: Event) => this.arrowsAction(ArrowDirection.LEFT, e))
);
}
}
private bindEvent(event: string, action: (...args: any[]) => void) {
if (this.eventOwner) {
this.eventOwner.on(this.element, event, action);
} else {
$$(this.element).on(event, action);
}
}
}