bitmovin-player-ui
Version:
Bitmovin Player UI Framework
218 lines (187 loc) • 6.48 kB
text/typescript
import { Button, ButtonConfig } from './Button';
import { NoArgs, EventDispatcher, Event } from '../../EventDispatcher';
import { UIInstanceManager } from '../../UIManager';
import { PlayerAPI } from 'bitmovin-player';
import { LocalizableText } from '../../localization/i18n';
/**
* Configuration interface for a toggle button component.
*
* @category Configs
*/
export interface ToggleButtonConfig extends ButtonConfig {
/**
* The CSS class that marks the on-state of the button.
*/
onClass?: string;
/**
* The CSS class that marks the off-state of the button.
*/
offClass?: string;
/**
* WCAG20 standard for defining info about the component (usually the name)
*
* It is recommended to use `onAriaLabel` and `offAriaLabel` for toggle buttons
* as the component can then update them as the button is used. If this is the case,
* the `aria-pressed` attribute is not set as the specification defines either the name
* or the pressed-state should be changed, but not both.
* Some platforms, like many versions of Samsung Tizen TVs, don't support the `aria-pressed`
* attribute, so it is recommended to use `onAriaLabel` and `offAriaLabel` instead.
*
* If both `ariaLabel` and `onAriaLabel` are set, `onAriaLabel` is used.
*/
ariaLabel?: LocalizableText;
/**
* The aria label that marks the on-state of the button.
*/
onAriaLabel?: LocalizableText;
/**
* The aria label that marks the off-state of the button.
*/
offAriaLabel?: LocalizableText;
/**
* The text as string or as localize callback on the button.
*/
text?: LocalizableText;
}
/**
* A button that can be toggled between 'on' and 'off' states.
*
* @category Components
*/
export class ToggleButton<Config extends ToggleButtonConfig> extends Button<Config> {
private onState: boolean;
private useAriaPressedAttributeAsToggleIndicator: boolean;
private toggleButtonEvents = {
onToggle: new EventDispatcher<ToggleButton<Config>, NoArgs>(),
onToggleOn: new EventDispatcher<ToggleButton<Config>, NoArgs>(),
onToggleOff: new EventDispatcher<ToggleButton<Config>, NoArgs>(),
};
constructor(config: Config) {
super(config);
const defaultConfig: ToggleButtonConfig = {
cssClass: 'ui-togglebutton',
onClass: 'on',
offClass: 'off',
};
if (config.onAriaLabel) {
config.ariaLabel = config.onAriaLabel;
}
this.config = this.mergeConfig(config, defaultConfig as Config, this.config);
this.useAriaPressedAttributeAsToggleIndicator = !this.config.onAriaLabel || !this.config.offAriaLabel;
}
configure(player: PlayerAPI, uimanager: UIInstanceManager): void {
super.configure(player, uimanager);
const config = this.getConfig();
this.getDomElement().addClass(this.prefixCss(config.offClass));
this.useAriaPressedAttributeAsToggleIndicator = !this.config.onAriaLabel || !this.config.offAriaLabel;
if (this.useAriaPressedAttributeAsToggleIndicator) {
/**
* WCAG20 standard to display if a button is pressed or not. In screenreaders, this converts the button to a
* toggle button and the button label should not change. Unfortunately, not all devices and screenreaders support
* this attribute (e.g. Samsung Tizen TVs).
*
* The alternative is to not use this attribute and toggle the button
* label instead. This option will be used if both, `onAriaLabel` and `offAriaLabel` are set.
*/
this.setAriaAttr('pressed', 'false');
} else {
this.setAriaLabel(this.config.offAriaLabel);
}
}
/**
* Toggles the button to the 'on' state.
*/
on() {
if (this.isOff()) {
const config = this.getConfig();
this.onState = true;
this.getDomElement().removeClass(this.prefixCss(config.offClass));
this.getDomElement().addClass(this.prefixCss(config.onClass));
this.onToggleEvent();
this.onToggleOnEvent();
if (this.useAriaPressedAttributeAsToggleIndicator) {
this.setAriaAttr('pressed', 'true');
} else {
this.setAriaLabel(this.config.onAriaLabel);
}
}
}
/**
* Toggles the button to the 'off' state.
*/
off() {
if (this.isOn()) {
const config = this.getConfig();
this.onState = false;
this.getDomElement().removeClass(this.prefixCss(config.onClass));
this.getDomElement().addClass(this.prefixCss(config.offClass));
this.onToggleEvent();
this.onToggleOffEvent();
if (this.useAriaPressedAttributeAsToggleIndicator) {
this.setAriaAttr('pressed', 'false');
} else {
this.setAriaLabel(this.config.offAriaLabel);
}
}
}
/**
* Toggle the button 'on' if it is 'off', or 'off' if it is 'on'.
*/
toggle() {
if (this.isOn()) {
this.off();
} else {
this.on();
}
}
/**
* Checks if the toggle button is in the 'on' state.
* @returns {boolean} true if button is 'on', false if 'off'
*/
isOn(): boolean {
return this.onState;
}
/**
* Checks if the toggle button is in the 'off' state.
* @returns {boolean} true if button is 'off', false if 'on'
*/
isOff(): boolean {
return !this.isOn();
}
protected onClickEvent() {
super.onClickEvent();
// Fire the toggle event together with the click event
// (they are technically the same, only the semantics are different)
this.onToggleEvent();
}
protected onToggleEvent() {
this.toggleButtonEvents.onToggle.dispatch(this);
}
protected onToggleOnEvent() {
this.toggleButtonEvents.onToggleOn.dispatch(this);
}
protected onToggleOffEvent() {
this.toggleButtonEvents.onToggleOff.dispatch(this);
}
/**
* Gets the event that is fired when the button is toggled.
* @returns {Event<ToggleButton<Config>, NoArgs>}
*/
get onToggle(): Event<ToggleButton<Config>, NoArgs> {
return this.toggleButtonEvents.onToggle.getEvent();
}
/**
* Gets the event that is fired when the button is toggled 'on'.
* @returns {Event<ToggleButton<Config>, NoArgs>}
*/
get onToggleOn(): Event<ToggleButton<Config>, NoArgs> {
return this.toggleButtonEvents.onToggleOn.getEvent();
}
/**
* Gets the event that is fired when the button is toggled 'off'.
* @returns {Event<ToggleButton<Config>, NoArgs>}
*/
get onToggleOff(): Event<ToggleButton<Config>, NoArgs> {
return this.toggleButtonEvents.onToggleOff.getEvent();
}
}