From 437cee56693f69b2a58f37f8a5ff5b931ea803a2 Mon Sep 17 00:00:00 2001 From: Shulhan Date: Sun, 5 Sep 2021 15:28:30 +0700 Subject: input: implement class for input number The WuiInputNumber create an HTML input that allow number only, with optional max and min options. The required options is "label" and "value". Format of generated HTML output,
i
${hint}
User can set onChangeHandler to receive new value when the value changes and valid; otherwise, if the value is invalid, the input background will changes accordingly. --- index.html | 10 +++ input/example.d.ts | 1 + input/example.ts | 69 +++++++++++++++++++++ input/index.html | 12 ++++ input/number.d.ts | 26 ++++++++ input/number.ts | 177 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 295 insertions(+) create mode 100644 index.html create mode 100644 input/example.d.ts create mode 100644 input/example.ts create mode 100644 input/index.html create mode 100644 input/number.d.ts create mode 100644 input/number.ts diff --git a/index.html b/index.html new file mode 100644 index 0000000..8e9a20c --- /dev/null +++ b/index.html @@ -0,0 +1,10 @@ + + + + + WUI - web user interface + + +

Input

+ + diff --git a/input/example.d.ts b/input/example.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/input/example.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/input/example.ts b/input/example.ts new file mode 100644 index 0000000..3e68451 --- /dev/null +++ b/input/example.ts @@ -0,0 +1,69 @@ +import { WuiInputNumber, WuiInputNumberOpts } from "./number.js" + +function exampleInputNumber() { + let el_example = document.createElement("div") + + let el_title = document.createElement("h3") + el_title.innerText = "Input number" + el_example.appendChild(el_title) + + let opts: WuiInputNumberOpts = { + label: "Input number", + value: 100, + onChangeHandler: (val: number) => { + console.log("number changes to", val) + }, + } + let input_num = new WuiInputNumber(opts) + el_example.appendChild(input_num.el) + + opts = { + id: "my_input_number", + label: "Input number with ID", + value: 100, + hint: "The ID for this input is 'my_input_number'", + onChangeHandler: (val: number) => { + console.log("number changes to", val) + }, + } + input_num = new WuiInputNumber(opts) + el_example.appendChild(input_num.el) + + opts = { + label: "Input number disabled", + value: 100, + hint: "Input number with 'is_disabled' set to true", + is_disabled: true, + onChangeHandler: (val: number) => { + console.log("number changes to", val) + }, + } + input_num = new WuiInputNumber(opts) + el_example.appendChild(input_num.el) + + opts = { + label: "Input number with hint", + value: 100, + hint: "This is the hint", + onChangeHandler: (val: number) => { + console.log("number changes to", val) + }, + } + input_num = new WuiInputNumber(opts) + el_example.appendChild(input_num.el) + + opts = { + label: "Input number with max and min", + value: 10, + max: 12, + min: -20, + onChangeHandler: (val: number) => { + console.log("number changes to", val) + }, + } + input_num = new WuiInputNumber(opts) + el_example.appendChild(input_num.el) + document.body.appendChild(el_example) +} + +exampleInputNumber() diff --git a/input/index.html b/input/index.html new file mode 100644 index 0000000..714932f --- /dev/null +++ b/input/index.html @@ -0,0 +1,12 @@ + + + + + WUI - input + + + + + + diff --git a/input/number.d.ts b/input/number.d.ts new file mode 100644 index 0000000..cbf847a --- /dev/null +++ b/input/number.d.ts @@ -0,0 +1,26 @@ +export interface WuiInputNumberOpts { + label: string; + value: number; + id?: string; + hint?: string; + max?: number; + min?: number; + is_disabled?: boolean; + onChangeHandler: (new_value: number) => void; +} +export declare class WuiInputNumber { + opts: WuiInputNumberOpts; + el: HTMLElement; + private el_label; + private el_input; + private el_hint; + private el_hint_toggler; + private value; + constructor(opts: WuiInputNumberOpts); + private generateLabel; + private generateInput; + private generateHintToggler; + private generateHint; + private onClickHintToggler; + private onKeyUp; +} diff --git a/input/number.ts b/input/number.ts new file mode 100644 index 0000000..11e561b --- /dev/null +++ b/input/number.ts @@ -0,0 +1,177 @@ +export interface WuiInputNumberOpts { + label: string + value: number + id?: string + hint?: string + max?: number + min?: number + is_disabled?: boolean + onChangeHandler: (new_value: number) => void +} + +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, +// +//
+//
+// +// +// [ i] +//
+// [
${hint}
] +//
+// +// 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 { + el: HTMLElement + private el_label!: HTMLElement + private el_input!: HTMLInputElement + private el_hint!: HTMLElement + private el_hint_toggler!: HTMLElement + private value: number = 0 + + constructor(public opts: WuiInputNumberOpts) { + 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" + + let 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() + } + } + + private generateLabel(wrapper: HTMLElement) { + this.el_label = document.createElement("label") + this.el_label.classList.add(WUI_INPUT_NUMBER_CLASS_LABEL) + this.el_label.innerHTML = `${this.opts.label} ` + wrapper.appendChild(this.el_label) + } + + private generateInput(wrapper: HTMLElement) { + this.el_input = document.createElement("input") as HTMLInputElement + this.el_input.classList.add(WUI_INPUT_NUMBER_CLASS_INPUT) + this.el_input.type = "number" + 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: KeyboardEvent) => { + return this.onKeyUp(ev) + } + + wrapper.appendChild(this.el_input) + } + + private generateHintToggler(wrapper: HTMLElement) { + 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) + } + + private generateHint() { + let 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 + this.el_hint.style.display = "none" + this.el_hint.style.backgroundColor = "gainsboro" + this.el_hint.style.borderRadius = "2px" + this.el_hint.style.padding = "4px" + this.el.appendChild(this.el_hint) + } + + private onClickHintToggler() { + if (this.el_hint.style.display === "none") { + this.el_hint.style.display = "block" + } else { + this.el_hint.style.display = "none" + } + } + + private onKeyUp(ev: KeyboardEvent) { + let target = ev.target as HTMLInputElement + + ev.preventDefault() + let newValue = parseInt(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 + } +} -- cgit v1.3