diff options
Diffstat (limited to 'input/number.js')
| -rw-r--r-- | input/number.js | 170 |
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 = " ℹ"; + 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; + } +} |
