aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2021-09-05 15:28:30 +0700
committerShulhan <ms@kilabit.info>2021-09-05 18:36:42 +0700
commit437cee56693f69b2a58f37f8a5ff5b931ea803a2 (patch)
tree540378dcb05d299644d4679db85e80a1731fb9bd
parent865173ec06c86ebb900c22b066dcf210f2e0452d (diff)
downloadpakakeh.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.
-rw-r--r--index.html10
-rw-r--r--input/example.d.ts1
-rw-r--r--input/example.ts69
-rw-r--r--input/index.html12
-rw-r--r--input/number.d.ts26
-rw-r--r--input/number.ts177
6 files changed, 295 insertions, 0 deletions
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..8e9a20c
--- /dev/null
+++ b/index.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="UTF-8" />
+ <title>WUI - web user interface</title>
+ </head>
+ <body>
+ <h3><a href="/input"> Input </a></h3>
+ </body>
+</html>
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 = " &#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)
+ }
+
+ 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
+ }
+}