pvbemu/app/toolkit/Button.js

245 lines
6.9 KiB
JavaScript
Raw Normal View History

2021-08-23 23:56:36 +00:00
"use strict";
// Clickable button
Toolkit.Button = class Button extends Toolkit.Component {
// Object constructor
constructor(application, options) {
super(application, "div");
options = options || {};
// Configure instance fields
this.blurListeners = [];
this.clickListeners = [];
this.enabled = "enabled" in options ? options.enabled : true;
this.focusListeners = [];
this.pressed = "pressed" in options ? options.pressed : false;
this.tabStop = true;
this.text = options.text || "";
this.toggleable = "toggleable"in options?options.toggleable:false;
// Configure element
this.element.type = "button";
this.element.setAttribute("role", "button");
this.element.style.cursor = "default";
this.element.style.position = "relative";
this.element.style.userSelect = "none";
this.element.addEventListener("blur" , e=>this.onblur (e));
this.element.addEventListener("focus" , e=>this.onfocus (e));
this.element.addEventListener("keydown" , e=>this.onkeydown (e));
this.element.addEventListener("pointerdown", e=>this.onpointerdown(e));
this.element.addEventListener("pointermove", e=>this.onpointermove(e));
this.element.addEventListener("pointerup" , e=>this.onpointerup (e));
// Configure properties
this.setEnabled (this.enabled );
this.setPressed (this.pressed );
this.setTabStop (this.tabStop );
this.setText (this.text );
this.setToggleable(this.toggleable);
application.addComponent(this);
}
///////////////////////////// Public Methods //////////////////////////////
// Add a callback for blur events
addBlurListener(listener) {
if (this.blurListeners.indexOf(listener) == -1)
this.blurListeners.push(listener);
}
// Add a callback for click events
addClickListener(listener) {
if (this.clickListeners.indexOf(listener) == -1)
this.clickListeners.push(listener);
}
// Add a callback for focus events
addFocusListener(listener) {
if (this.focusListeners.indexOf(listener) == -1)
this.focusListeners.push(listener);
}
// Determine whether the component participates in the tab sequence
getTabStop() {
return this.tabStop;
}
// Retrieve the control's text
getText() {
return this.text;
}
// Determine whether the control is enabled
isEnabled() {
return this.enabled;
}
// Determine the toggle button's active state
isPressed() {
return this.pressed;
}
// Determine whether the button is a toggle button
isToggleable() {
return this.toggleable;
}
// Specify whether the control is enabled
setEnabled(enabled) {
this.enabled = enabled = !!enabled;
if (enabled)
this.element.removeAttribute("disabled");
else this.element.setAttribute("disabled", "");
}
// Specify whether the component participates in the regular tab sequence
setTabStop(tabStop) {
this.tabStop = tabStop = !!tabStop;
this.element.setAttribute("tabindex", tabStop ? "0" : "-1");
}
// Specify the toggle button's active state
setPressed(pressed) {
this.pressed = pressed = !!pressed;
if (this.toggleable)
this.element.setAttribute("aria-pressed", pressed);
}
// Specify the control's text
setText(text) {
this.text = text || "";
this.localize();
}
// Specify whether the button is a toggle button
setToggleable(toggleable) {
this.toggleable = toggleable = !!toggleable;
if (toggleable)
this.element.setAttribute("aria-pressed", this.pressed);
else this.element.removeAttribute("aria-pressed");
}
///////////////////////////// Private Methods /////////////////////////////
// Actions when the button is activated
activate(e) {
if (this.toggleable)
this.setPressed(!this.pressed);
for (let listener of this.clickListeners)
listener(e, this);
}
// Update display text with localized strings
localize() {
let text = this.text;
if (this.application)
text = this.application.translate(text, this);
this.element.innerText = text;
this.element.setAttribute("aria-label", text);
}
// Blur event handler
onblur(e) {
for (let listener of this.blurListeners)
listener(e, this);
this.focusChanged(
this, e.relatedTarget ? e.relatedTarget.component : null);
}
// Focus event handler
onfocus(e) {
for (let listener of this.focusListeners)
listener(e, this);
this.focusChanged(
e.relatedTarget ? e.relatedTarget.component : null, this);
}
// Key press event handler
onkeydown(e) {
// Error checking
if (e.key != " " && e.key != "Enter")
return;
// Configure event
e.preventDefault();
e.stopPropagation();
// The button was activated
this.activate(e);
}
// Pointer down event handler
onpointerdown(e) {
// Error checking
if (e.button != 0 || this.element.hasPointerCapture(e.pointerId))
return;
// Configure event
e.preventDefault();
e.stopPropagation();
// Configure element
this.element.focus();
this.element.setPointerCapture(e.pointerId);
this.element.setAttribute("active", "");
}
// Pointer move event handler
onpointermove(e) {
// Error checking
if (!this.element.hasPointerCapture(e.pointerId))
return;
// Configure event
e.preventDefault();
e.stopPropagation();
// Working variables
let bounds = this.element.getBoundingClientRect();
let active =
e.x >= bounds.x && e.x < bounds.x + bounds.width &&
e.y >= bounds.y && e.y < bounds.y + bounds.height
;
// Configure event
if (active)
this.element.setAttribute("active", "");
else this.element.removeAttribute("active");
}
// Pointer up event handler
onpointerup(e) {
// Error checking
if (!this.element.hasPointerCapture(e.pointerId))
return;
// Configure event
e.preventDefault();
e.stopPropagation();
// Working variables
let active = this.element.hasAttribute("active");
// Configure element
this.element.releasePointerCapture(e.pointerId);
this.element.removeAttribute("active");
// The pointer was released without activating the button
if (!active)
return;
// The button was activated
this.activate(e);
}
};