diff options
| author | Shulhan <ms@kilabit.info> | 2021-09-05 15:28:30 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2021-09-05 18:36:42 +0700 |
| commit | 437cee56693f69b2a58f37f8a5ff5b931ea803a2 (patch) | |
| tree | 540378dcb05d299644d4679db85e80a1731fb9bd /input | |
| parent | 865173ec06c86ebb900c22b066dcf210f2e0452d (diff) | |
| download | pakakeh.ts-437cee56693f69b2a58f37f8a5ff5b931ea803a2.tar.xz | |
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,
<div [id=${id}] class="${WUI_INPUT_NUMBER_CLASS}">
<div>
<label class="${WUI_INPUT_NUMBER_CLASS_LABEL}">${label}</label>
<input
class="${WUI_INPUT_NUMBER_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 value
changes and valid; otherwise, if the value is invalid, the input
background will changes accordingly.
Diffstat (limited to 'input')
| -rw-r--r-- | input/example.d.ts | 1 | ||||
| -rw-r--r-- | input/example.ts | 69 | ||||
| -rw-r--r-- | input/index.html | 12 | ||||
| -rw-r--r-- | input/number.d.ts | 26 | ||||
| -rw-r--r-- | input/number.ts | 177 |
5 files changed, 285 insertions, 0 deletions
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 <b>hint</b>", + 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 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="UTF-8" /> + <title>WUI - input</title> + <style> + </style> + </head> + <body> + <script type="module" src="/input/example.js"></script> + </body> +</html> 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, +// +// <div [id=${id}] class="${WUI_INPUT_NUMBER_CLASS}"> +// <div> +// <label class="${WUI_INPUT_NUMBER_CLASS_LABEL}">${label}</label> +// <input +// class="${WUI_INPUT_NUMBER_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 { + 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 + } +} |
