aboutsummaryrefslogtreecommitdiff
path: root/input/number.js
diff options
context:
space:
mode:
Diffstat (limited to 'input/number.js')
-rw-r--r--input/number.js170
1 files changed, 170 insertions, 0 deletions
diff --git a/input/number.js b/input/number.js
new file mode 100644
index 0000000..135066c
--- /dev/null
+++ b/input/number.js
@@ -0,0 +1,170 @@
+// SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info>
+// SPDX-License-Identifier: GPL-3.0-or-later
+const WUI_INPUT_NUMBER_CLASS = "wui_input_number";
+const WUI_INPUT_NUMBER_CLASS_HINT = "wui_input_number_hint";
+const WUI_INPUT_NUMBER_CLASS_HINT_TOGGLER = "wui_input_number_hint_toggler";
+const WUI_INPUT_NUMBER_CLASS_INPUT = "wui_input_number_input";
+const WUI_INPUT_NUMBER_CLASS_LABEL = "wui_input_number_label";
+//
+// WuiInputNumber create an HTML input that allow number only, with optional
+// max and min options.
+// The required options are "label" and "value".
+//
+// Format of generated HTML output,
+//
+// <div [id=${id}] class="${WUI_INPUT_NUMBER_CLASS}">
+// <div>
+// <label class="${WUI_INPUT_NUMBER_CLASS_LABEL} [${class_label}]">
+// ${label} | HTMLElement
+// </label>
+// <input
+// class="${WUI_INPUT_NUMBER_CLASS_INPUT} [${class_input}]"
+// [max=${max}]
+// [min=${min}]
+// [disabled=${is_disabled}]
+// value=${value}
+// >
+// [<span class="${WUI_INPUT_NUMBER_CLASS_HINT_TOGGLER}"> i</span>]
+// </div>
+// [<div class="${WUI_INPUT_NUMBER_CLASS_HINT}">${hint}</div>]
+// </div>
+//
+// User can set onChangeHandler to receive new value when the input value
+// changes and valid; otherwise, if the value is invalid, the input
+// background will changes accordingly.
+//
+export class WuiInputNumber {
+ constructor(opts) {
+ this.opts = opts;
+ this.value = 0;
+ this.value = opts.value;
+ this.el = document.createElement("div");
+ if (opts.id) {
+ this.el.id = opts.id;
+ }
+ this.el.classList.add(WUI_INPUT_NUMBER_CLASS);
+ this.el.style.padding = "2px";
+ const wrapper = document.createElement("div");
+ this.generateLabel(wrapper);
+ this.generateInput(wrapper);
+ if (opts.hint) {
+ this.generateHintToggler(wrapper);
+ }
+ this.el.appendChild(wrapper);
+ if (opts.hint) {
+ this.generateHint();
+ }
+ }
+ generateLabel(wrapper) {
+ this.el_label = document.createElement("label");
+ this.el_label.classList.add(WUI_INPUT_NUMBER_CLASS_LABEL);
+ if (this.opts.class_label) {
+ this.el_label.classList.add(this.opts.class_label);
+ }
+ if (typeof this.opts.label === "string") {
+ this.el_label.innerHTML = `${this.opts.label} `;
+ }
+ else {
+ this.el_label.appendChild(this.opts.label);
+ }
+ wrapper.appendChild(this.el_label);
+ }
+ generateInput(wrapper) {
+ this.el_input = document.createElement("input");
+ this.el_input.classList.add(WUI_INPUT_NUMBER_CLASS_INPUT);
+ if (this.opts.class_input) {
+ this.el_input.classList.add(this.opts.class_input);
+ }
+ this.el_input.type = "number";
+ this.el_input.step = "any";
+ this.el_input.value = "" + this.opts.value;
+ let hint = "";
+ if (this.opts.max) {
+ this.el_input.max = "" + this.opts.max;
+ hint = "The maximum value is " + this.opts.max;
+ }
+ if (this.opts.min) {
+ this.el_input.min = "" + this.opts.min;
+ if (hint == "") {
+ hint = "The ";
+ }
+ else {
+ hint += " and ";
+ }
+ hint += "minimum value is " + this.opts.min;
+ }
+ if (hint !== "") {
+ this.el_input.title = hint;
+ }
+ if (this.opts.is_disabled) {
+ this.el_input.disabled = true;
+ }
+ this.el_input.onkeyup = (ev) => {
+ return this.onKeyUp(ev);
+ };
+ wrapper.appendChild(this.el_input);
+ }
+ generateHintToggler(wrapper) {
+ this.el_hint_toggler = document.createElement("span");
+ this.el_hint_toggler.classList.add(WUI_INPUT_NUMBER_CLASS_HINT_TOGGLER);
+ this.el_hint_toggler.innerHTML = " &#x2139;";
+ this.el_hint_toggler.onmouseover = () => {
+ this.el_hint_toggler.style.cursor = "pointer";
+ };
+ this.el_hint_toggler.onclick = () => {
+ this.onClickHintToggler();
+ };
+ wrapper.appendChild(this.el_hint_toggler);
+ }
+ generateHint() {
+ const hint = this.opts.hint || "";
+ this.el_hint = document.createElement("div");
+ this.el_hint.classList.add(WUI_INPUT_NUMBER_CLASS_HINT);
+ this.el_hint.innerHTML = hint;
+ if (this.opts.is_hint_toggled) {
+ this.el_hint.style.display = "block";
+ }
+ else {
+ this.el_hint.style.display = "none";
+ }
+ this.el_hint.style.borderRadius = "2px";
+ this.el_hint.style.padding = "4px";
+ this.el_hint.style.marginTop = "2px";
+ this.el.appendChild(this.el_hint);
+ }
+ onClickHintToggler() {
+ if (this.el_hint.style.display === "none") {
+ this.el_hint.style.display = "block";
+ }
+ else {
+ this.el_hint.style.display = "none";
+ }
+ }
+ onKeyUp(ev) {
+ const target = ev.target;
+ ev.preventDefault();
+ const newValue = +target.value;
+ if (newValue === null) {
+ this.el_input.style.backgroundColor = "lightsalmon";
+ return false;
+ }
+ if (this.opts.max && newValue > this.opts.max) {
+ this.el_input.style.backgroundColor = "lightsalmon";
+ return false;
+ }
+ if (this.opts.min && newValue < this.opts.min) {
+ this.el_input.style.backgroundColor = "lightsalmon";
+ return false;
+ }
+ this.el_input.style.backgroundColor = "white";
+ this.value = newValue;
+ if (this.opts.onChangeHandler) {
+ this.opts.onChangeHandler(this.value);
+ }
+ return true;
+ }
+ // set the input value.
+ set(v) {
+ this.el_input.value = "" + v;
+ }
+}