aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
+ }
+}