2022-04-15 01:51:09 +00:00
|
|
|
import { Component } from /**/"./Component.js";
|
|
|
|
let Toolkit;
|
2021-08-23 23:56:36 +00:00
|
|
|
|
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Button //
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// Push, toggle or radio button
|
|
|
|
class Button extends Component {
|
|
|
|
static Component = Component;
|
|
|
|
|
|
|
|
//////////////////////////////// Constants ////////////////////////////////
|
|
|
|
|
|
|
|
// Types
|
|
|
|
static BUTTON = 0;
|
|
|
|
static RADIO = 1;
|
|
|
|
static TOGGLE = 2;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////// Initialization Methods //////////////////////////
|
|
|
|
|
|
|
|
constructor(gui, options) {
|
|
|
|
super(gui, options, {
|
|
|
|
className: "tk tk-button",
|
|
|
|
focusable: true,
|
|
|
|
role : "button",
|
|
|
|
tagName : "div",
|
|
|
|
style : {
|
|
|
|
display : "inline-block",
|
|
|
|
userSelect: "none"
|
|
|
|
}
|
|
|
|
});
|
2021-08-23 23:56:36 +00:00
|
|
|
|
|
|
|
// Configure instance fields
|
2022-04-15 01:51:09 +00:00
|
|
|
options = options || {};
|
|
|
|
this.attribute = options.attribute || "aria-pressed";
|
|
|
|
this.group = null;
|
|
|
|
this.isEnabled = null;
|
|
|
|
this.isSelected = false;
|
|
|
|
this.text = null;
|
|
|
|
this.type = Button.BUTTON;
|
|
|
|
|
|
|
|
// Configure contents
|
|
|
|
this.contents = document.createElement("div");
|
|
|
|
this.append(this.contents);
|
|
|
|
|
|
|
|
// Configure component
|
|
|
|
this.setEnabled(!("enabled" in options) || options.enabled);
|
|
|
|
if ("group" in options)
|
|
|
|
options.group.add(this);
|
|
|
|
this.setText (options.text);
|
|
|
|
this.setType (options.type);
|
|
|
|
if ("selected" in options)
|
|
|
|
this.setSelected(options.selected);
|
|
|
|
|
|
|
|
// Configure event handlers
|
|
|
|
this.addEventListener("keydown" , e=>this.onKeyDown (e));
|
|
|
|
this.addEventListener("pointerdown", e=>this.onPointerDown(e));
|
|
|
|
this.addEventListener("pointermove", e=>this.onPointerMove(e));
|
|
|
|
this.addEventListener("pointerup" , e=>this.onPointerUp (e));
|
2021-08-23 23:56:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
///////////////////////////// Event Handlers //////////////////////////////
|
2021-08-23 23:56:36 +00:00
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
// Key press
|
|
|
|
onKeyDown(e) {
|
2021-08-23 23:56:36 +00:00
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
// Processing by key
|
|
|
|
switch (e.key) {
|
|
|
|
case "Enter": // Fallthrough
|
|
|
|
case " " :
|
|
|
|
this.click();
|
|
|
|
break;
|
|
|
|
default: return;
|
|
|
|
}
|
2021-09-02 00:16:22 +00:00
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
// Configure event
|
|
|
|
e.stopPropagation();
|
|
|
|
e.preventDefault();
|
2021-08-23 23:56:36 +00:00
|
|
|
}
|
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
// Pointer down
|
|
|
|
onPointerDown(e) {
|
|
|
|
this.focus();
|
|
|
|
|
|
|
|
// Error checking
|
|
|
|
if (
|
|
|
|
!this.isEnabled ||
|
|
|
|
this.element.hasPointerCapture(e.pointerId) ||
|
|
|
|
e.button != 0
|
|
|
|
) return;
|
2021-08-23 23:56:36 +00:00
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
// Configure event
|
|
|
|
this.element.setPointerCapture(e.pointerId);
|
|
|
|
e.stopPropagation();
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
// Configure component
|
|
|
|
this.element.classList.add("active");
|
2021-08-23 23:56:36 +00:00
|
|
|
}
|
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
// Pointer move
|
|
|
|
onPointerMove(e) {
|
|
|
|
|
|
|
|
// Error checking
|
|
|
|
if (!this.element.hasPointerCapture(e.pointerId))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Configure event
|
|
|
|
e.stopPropagation();
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
// Configure component
|
|
|
|
this.element.classList[
|
|
|
|
Toolkit.isInside(this.element, e) ? "add" : "remove"]("active");
|
2021-08-23 23:56:36 +00:00
|
|
|
}
|
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
// Pointer up
|
|
|
|
onPointerUp(e) {
|
|
|
|
|
|
|
|
// Error checking
|
|
|
|
if (
|
|
|
|
!this.isEnabled ||
|
|
|
|
e.button != 0 ||
|
|
|
|
!this.element.hasPointerCapture(e.pointerId)
|
|
|
|
) return;
|
|
|
|
|
|
|
|
// Configure event
|
|
|
|
this.element.releasePointerCapture(e.pointerId);
|
|
|
|
e.stopPropagation();
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
// Configure component
|
|
|
|
this.element.classList.remove("active");
|
|
|
|
|
|
|
|
// Item is an action
|
|
|
|
let bounds = this.getBounds();
|
|
|
|
if (this.menu == null && Toolkit.isInside(this.element, e))
|
|
|
|
this.click();
|
2021-08-23 23:56:36 +00:00
|
|
|
}
|
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////// Public Methods //////////////////////////////
|
|
|
|
|
|
|
|
// Programmatically activate the button
|
|
|
|
click() {
|
|
|
|
if (this instanceof Toolkit.CheckBox)
|
|
|
|
this.setSelected(this instanceof Toolkit.Radio||!this.isSelected);
|
|
|
|
this.event("action");
|
2021-08-23 23:56:36 +00:00
|
|
|
}
|
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
// Specify whether the button can be activated
|
2021-08-23 23:56:36 +00:00
|
|
|
setEnabled(enabled) {
|
2022-04-15 01:51:09 +00:00
|
|
|
this.isEnabled = enabled = !!enabled;
|
|
|
|
this.setAttribute("aria-disabled", enabled ? null : "true");
|
2021-08-23 23:56:36 +00:00
|
|
|
}
|
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
// Specify whether the toggle or radio button is selected
|
|
|
|
setSelected(selected) {
|
|
|
|
selected = !!selected;
|
|
|
|
|
|
|
|
// Take no action
|
|
|
|
if (selected == this.isSelected)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Processing by button type
|
|
|
|
switch (this.type) {
|
|
|
|
case Button.RADIO :
|
|
|
|
if (selected && this.group != null)
|
|
|
|
this.group.deselect();
|
|
|
|
// Fallthrough
|
|
|
|
case Button.TOGGLE:
|
|
|
|
this.isSelected = selected;
|
|
|
|
this.setAttribute(this.attribute, selected);
|
|
|
|
}
|
2021-08-23 23:56:36 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
// Specify the widget's display text
|
2021-08-23 23:56:36 +00:00
|
|
|
setText(text) {
|
2022-04-15 01:51:09 +00:00
|
|
|
this.text = (text || "").toString().trim();
|
|
|
|
this.translate();
|
2021-08-23 23:56:36 +00:00
|
|
|
}
|
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
// Specify what kind of button this is
|
|
|
|
setType(type) {
|
|
|
|
switch (type) {
|
|
|
|
case Button.BUTTON:
|
|
|
|
this.type = type;
|
|
|
|
this.setAttribute(this.attribute, null);
|
|
|
|
this.setSelected(false);
|
|
|
|
break;
|
|
|
|
case Button.RADIO : // Fallthrough
|
|
|
|
case Button.TOGGLE:
|
|
|
|
this.type = type;
|
|
|
|
this.setAttribute(this.attribute, this.isSelected);
|
|
|
|
this.setSelected(this.isSelected);
|
|
|
|
break;
|
|
|
|
default: return;
|
|
|
|
}
|
2021-08-23 23:56:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-08-26 19:23:18 +00:00
|
|
|
///////////////////////////// Package Methods /////////////////////////////
|
2021-08-23 23:56:36 +00:00
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
// Update the global Toolkit object
|
|
|
|
static setToolkit(toolkit) {
|
|
|
|
Toolkit = toolkit;
|
2021-08-23 23:56:36 +00:00
|
|
|
}
|
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
// Regenerate localized display text
|
|
|
|
translate() {
|
|
|
|
super.translate();
|
|
|
|
if (this.contents != null)
|
|
|
|
this.contents.innerText = this.gui.translate(this.text, this);
|
|
|
|
}
|
2021-08-23 23:56:36 +00:00
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
}
|
2021-08-26 19:23:18 +00:00
|
|
|
|
|
|
|
|
2021-08-23 23:56:36 +00:00
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// CheckBox //
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2021-08-23 23:56:36 +00:00
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
// On/off toggle box
|
|
|
|
class CheckBox extends Button {
|
2021-08-26 19:23:18 +00:00
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
///////////////////////// Initialization Methods //////////////////////////
|
|
|
|
|
|
|
|
constructor(app, options) {
|
|
|
|
|
|
|
|
// Default options override
|
|
|
|
let uptions = {};
|
|
|
|
Object.assign(uptions, options || {});
|
|
|
|
for (let entry of Object.entries({
|
|
|
|
attribute: "aria-checked",
|
|
|
|
className: "tk tk-checkbox",
|
|
|
|
role : "checkbox",
|
|
|
|
style : {},
|
|
|
|
type : Button.TOGGLE
|
|
|
|
})) if (!(entry[0] in uptions))
|
|
|
|
uptions[entry[0]] = entry[1];
|
|
|
|
|
|
|
|
// Default styles override
|
|
|
|
for (let entry of Object.entries({
|
|
|
|
display : "inline-grid",
|
|
|
|
gridTemplateColumns: "max-content auto"
|
|
|
|
})) if (!(entry[0] in uptions.style))
|
|
|
|
uptions.style[entry[0]] = entry[1];
|
|
|
|
|
|
|
|
// Component overrides
|
|
|
|
super(app, uptions);
|
|
|
|
this.contents.classList.add("tk-contents");
|
|
|
|
|
|
|
|
// Configure icon
|
|
|
|
this.icon = document.createElement("div");
|
|
|
|
this.icon.className = "tk tk-icon";
|
|
|
|
this.icon.setAttribute("aria-hidden", "true");
|
|
|
|
this.prepend(this.icon);
|
2021-08-23 23:56:36 +00:00
|
|
|
}
|
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
}
|
2021-08-23 23:56:36 +00:00
|
|
|
|
|
|
|
|
2021-08-26 19:23:18 +00:00
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Radio //
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2021-08-26 19:23:18 +00:00
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
// Single selection box
|
|
|
|
class Radio extends CheckBox {
|
|
|
|
|
|
|
|
///////////////////////// Initialization Methods //////////////////////////
|
|
|
|
|
|
|
|
constructor(app, options) {
|
|
|
|
|
|
|
|
// Default options override
|
|
|
|
let uptions = {};
|
|
|
|
Object.assign(uptions, options || {});
|
|
|
|
for (let entry of Object.entries({
|
|
|
|
className: "tk tk-radio",
|
|
|
|
role : "radio",
|
|
|
|
type : Button.RADIO
|
|
|
|
})) if (!(entry[0] in uptions))
|
|
|
|
uptions[entry[0]] = entry[1];
|
|
|
|
|
|
|
|
// Component overrides
|
|
|
|
super(app, uptions);
|
2021-08-23 23:56:36 +00:00
|
|
|
}
|
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
}
|
2021-08-23 23:56:36 +00:00
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Group //
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// Radio button or menu item group
|
|
|
|
class Group extends Component {
|
|
|
|
|
|
|
|
///////////////////////// Initialization Methods //////////////////////////
|
|
|
|
|
|
|
|
constructor(app) {
|
|
|
|
super(app, {
|
|
|
|
tagName: "div",
|
|
|
|
style : {
|
|
|
|
height : "0",
|
|
|
|
position: "absolute",
|
|
|
|
width : "0"
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Configure instance fields
|
|
|
|
this.items = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////// Public Methods //////////////////////////////
|
|
|
|
|
|
|
|
// Add an item
|
|
|
|
add(item) {
|
2021-08-23 23:56:36 +00:00
|
|
|
|
2021-08-26 19:23:18 +00:00
|
|
|
// Error checking
|
2022-04-15 01:51:09 +00:00
|
|
|
if (!Toolkit.isComponent(item) || this.items.indexOf(item) != -1)
|
|
|
|
return item;
|
2021-08-26 19:23:18 +00:00
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
// Configure component
|
|
|
|
this.setAttribute("role",
|
|
|
|
item instanceof Toolkit.Radio ? "radiogroup" : "group");
|
2021-08-23 23:56:36 +00:00
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
// Configure item
|
|
|
|
if (item.group != null)
|
|
|
|
item.group.remove(item);
|
|
|
|
item.group = this;
|
|
|
|
|
|
|
|
// Add the item to the collection
|
|
|
|
item.id = item.id || Toolkit.id();
|
|
|
|
this.items.push(item);
|
|
|
|
this.setAttribute("aria-owns", this.items.map(i=>i.id).join(" "));
|
|
|
|
|
|
|
|
return item;
|
2021-08-23 23:56:36 +00:00
|
|
|
}
|
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
// Remove all items
|
|
|
|
clear() {
|
|
|
|
this.items.splice();
|
|
|
|
this.setAttribute("aria-owns", "");
|
|
|
|
}
|
2021-08-23 23:56:36 +00:00
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
// Un-check all items in the group
|
|
|
|
deselect() {
|
|
|
|
for (let item of this.items)
|
|
|
|
if (item.isSelected && "setSelected" in item)
|
|
|
|
item.setSelected(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove an item
|
|
|
|
remove(item) {
|
2021-08-23 23:56:36 +00:00
|
|
|
|
2021-08-26 19:23:18 +00:00
|
|
|
// Error checking
|
2022-04-15 01:51:09 +00:00
|
|
|
let index = this.items.indexOf(item);
|
|
|
|
if (index == -1)
|
2021-08-26 19:23:18 +00:00
|
|
|
return;
|
2021-08-23 23:56:36 +00:00
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
// Remove the item from the collection
|
|
|
|
this.items.splice(index, 1);
|
|
|
|
this.setAttribute("aria-owns", this.items.map(i=>i.id).join(" "));
|
|
|
|
item.group = null;
|
2021-08-23 23:56:36 +00:00
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
return item;
|
2021-08-23 23:56:36 +00:00
|
|
|
}
|
|
|
|
|
2022-04-15 01:51:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export { Button, CheckBox, Group, Radio };
|