import { Component } from "./component.js";
import { Label } from "./label.js";
import { Style } from "./style.js";
/**
* Creates a clickable checkbox with a label that toggles on and off when clicked.
* <div><img src="https://www.minicomps.org/images/checkbox.png"/></div>
* @example
* const panel = new Panel(document.body, 20, 20, 200, 200);
* new Checkbox(panel, 20, 20, "Check it", false, event => console.log(event.target.checked));
* @extends Component
*/
export class Checkbox extends Component {
/**
* Constructor
* @param {HTMLElement} parent - The element to add this checkbox to.
* @param {number} x - The x position of the checkbox.
* @param {number} y - The y position of the checkbox.
* @param {string} label - The label label of the checkbox.
* @param {boolean} checked - The initial checked state of the checkbox.
* @param {function} defaultHandler - A function that will handle the "click" event.
*/
constructor(parent, x, y, label, checked, defaultHandler) {
super(parent, x, y);
this._label = label;
this._createChildren();
this._createStyle();
this._createListeners();
this.setSize(100, 10);
this.setChecked(checked);
this.addEventListener("click", defaultHandler);
this._addToParent();
this._updateWidth();
}
//////////////////////////////////
// Core
//////////////////////////////////
_createChildren() {
this._setWrapperClass("MinimalCheckbox");
this._wrapper.tabIndex = 0;
this._check = this._createDiv(this._wrapper, "MinimalCheckboxCheck");
this._textLabel = new Label(this._wrapper, 15, 0, this._label);
}
_createStyle() {
const style = document.createElement("style");
style.textContent = Style.checkbox;
this.shadowRoot.append(style);
}
_createListeners() {
this._onClick = this._onClick.bind(this);
this._onKeyPress = this._onKeyPress.bind(this);
this._wrapper.addEventListener("click", this._onClick);
this._wrapper.addEventListener("keypress", this._onKeyPress);
}
//////////////////////////////////
// Handlers
//////////////////////////////////
_onClick(event) {
event.stopPropagation();
if (this._enabled) {
this.toggle();
this.dispatchEvent(new CustomEvent("click", { detail: this._checked }));
}
}
_onKeyPress(event) {
if (event.keyCode === 13 && this._enabled) {
this._wrapper.click();
}
}
//////////////////////////////////
// Private
//////////////////////////////////
_updateCheckStyle() {
let className = this._checked
? "MinimalCheckboxCheckChecked "
: "MinimalCheckboxCheck ";
if (!this._enabled) {
className += "MinimalCheckboxCheckDisabled";
}
this._check.setAttribute("class", className);
if (this._enabled) {
this._setWrapperClass("MinimalCheckbox");
} else {
this._setWrapperClass("MinimalCheckboxDisabled");
}
}
_updateWidth() {
this.style.width = this._textLabel.x + this._textLabel.width + "px";
}
//////////////////////////////////
// Public
//////////////////////////////////
/**
* Adds a handler function for the "click" event on this checkbox.
* @param {function} handler - A function that will handle the "click" event.
* @returns This instance, suitable for chaining.
*/
addHandler(handler) {
this.addEventListener("click", handler);
return this;
}
/**
* Automatically changes the value of a property on a target object with the main value of this component changes.
* @param {object} target - The target object to change.
* @param {string} prop - The string name of a property on the target object.
* @return This instance, suitable for chaining.
*/
bind(target, prop) {
this.addEventListener("click", event => {
target[prop] = event.detail;
});
return this;
}
/**
* Gets whether or not this checkbox is checked.
* @returns Whether or not this checkbox is checked.
*/
getChecked() {
return this._checked;
}
/** Gets the label on this checkbox.
* @returns The text of the label of this checkbox.
*/
getLabel() {
return this._label;
}
getWidth() {
return this._textLabel.x + this._textLabel.width;
}
/**
* Sets the checked state of this checkbox.
* @params {boolean} checked - Whether or not this checkbox will be checked.
* @returns This instance, suitable for chaining.
*/
setChecked(checked) {
this._checked = checked;
this._updateCheckStyle();
return this;
}
setEnabled(enabled) {
if (this._enabled === enabled) {
return this;
}
super.setEnabled(enabled);
this._updateCheckStyle();
this._textLabel.enabled = enabled;
if (this._enabled) {
this._wrapper.tabIndex = 0;
} else {
this._wrapper.tabIndex = -1;
}
return this;
}
setHeight(height) {
super.setHeight(height);
this._textLabel.height = height;
this._check.style.top = Math.round((this._height - 10) / 2) + "px";
return this;
}
/**
* Sets the label of this checkbox.
* @param {string} label - The label to set on this checkbox.
* @returns this instance, suitable for chaining.
*/
setLabel(label) {
this._label = label;
this._textLabel.text = label;
this._updateWidth();
return this;
}
/**
* Sets the width of this checkbox. In fact, setting the width does nothing because it is automatically determined by the width of the label.
*/
setWidth() {
return this;
}
/**
* Toggles the state of the checkbox between checked and not checked.
* @returns This instance, suitable for chaining.
*/
toggle() {
this.setChecked(!this._checked);
return this;
}
//////////////////////////////////
// Getters/Setters
// alphabetical. getter first.
//////////////////////////////////
/**
* Sets and gets the checked state of the checkbox.
*/
get checked() {
return this.getChecked();
}
set checked(checked) {
this.setChecked(checked);
}
/**
* Sets and gets the label shown in the button's label.
*/
get label() {
return this.getLabel();
}
set label(label) {
this.setLabel(label);
}
}
customElements.define("minimal-checkbox", Checkbox);