diff options
| -rw-r--r-- | input/example.ts | 45 | ||||
| -rw-r--r-- | input/option.d.ts | 4 | ||||
| -rw-r--r-- | input/option.ts | 4 | ||||
| -rw-r--r-- | input/select.d.ts | 33 | ||||
| -rw-r--r-- | input/select.ts | 171 |
5 files changed, 254 insertions, 3 deletions
diff --git a/input/example.ts b/input/example.ts index aa26d2d..f6a0647 100644 --- a/input/example.ts +++ b/input/example.ts @@ -1,5 +1,6 @@ import { WuiInputString, WuiInputStringOpts } from "./string.js" import { WuiInputNumber, WuiInputNumberOpts } from "./number.js" +import { WuiInputSelect, WuiInputSelectOpts } from "./select.js" function exampleInputString() { let el_example = document.createElement("div") @@ -15,7 +16,7 @@ function exampleInputString() { hint: "The input ID is 'my_input_string'", onChangeHandler: (new_value: string) => { console.log("input string new value: ", new_value) - } + }, } let el_input_string = new WuiInputString(opts) el_example.appendChild(el_input_string.el) @@ -27,7 +28,7 @@ function exampleInputString() { hint: "The input string is disabled", onChangeHandler: (new_value: string) => { console.log("input string new value: ", new_value) - } + }, } el_input_string = new WuiInputString(opts) el_example.appendChild(el_input_string.el) @@ -37,7 +38,7 @@ function exampleInputString() { value: "Hello, input string without hint", onChangeHandler: (new_value: string) => { console.log("input string without hint: new value: ", new_value) - } + }, } el_input_string = new WuiInputString(opts) el_example.appendChild(el_input_string.el) @@ -111,5 +112,43 @@ function exampleInputNumber() { document.body.appendChild(el_example) } +function exampleInputSelect() { + let el_example = document.createElement("div") + document.body.appendChild(el_example) + + let el_title = document.createElement("h3") + el_title.innerText = "Input select" + el_example.appendChild(el_title) + + let el_log = document.createElement("div") + + let opts: WuiInputSelectOpts = { + name: "my_fruit_price", + label: "Input select", + options: { + mango: { + value: "1000", + selected: false, + }, + papaya: { + value: "200", + selected: false, + }, + rambutan: { + value: "100", + selected: true, + }, + }, + hint: "Select on of the option to see the price.", + onChangeHandler: (key: string, value: string) => { + el_log.innerText = `The select input changes to '${key}' with price '${value}'` + }, + } + let input = new WuiInputSelect(opts) + el_example.appendChild(input.el) + el_example.appendChild(el_log) +} + exampleInputString() exampleInputNumber() +exampleInputSelect() diff --git a/input/option.d.ts b/input/option.d.ts new file mode 100644 index 0000000..ac723d2 --- /dev/null +++ b/input/option.d.ts @@ -0,0 +1,4 @@ +export interface WuiInputOption { + value: string; + selected: boolean; +} diff --git a/input/option.ts b/input/option.ts new file mode 100644 index 0000000..b13af53 --- /dev/null +++ b/input/option.ts @@ -0,0 +1,4 @@ +export interface WuiInputOption { + value: string + selected: boolean +} diff --git a/input/select.d.ts b/input/select.d.ts new file mode 100644 index 0000000..6a568f3 --- /dev/null +++ b/input/select.d.ts @@ -0,0 +1,33 @@ +import { WuiInputOption } from "./option.js"; +export interface WuiKeyValue { + [key: string]: string; +} +export interface WuiKeySelectOption { + [key: string]: WuiInputOption; +} +export interface WuiInputSelectOpts { + label: string; + name: string; + options: WuiKeySelectOption; + id?: string; + hint?: string; + is_disabled?: boolean; + onChangeHandler?: (key: string, value: string) => void; +} +export declare class WuiInputSelect { + opts: WuiInputSelectOpts; + el: HTMLElement; + private el_label; + private el_input; + private el_hint; + private el_hint_toggler; + private value_key; + private value; + constructor(opts: WuiInputSelectOpts); + private generateLabel; + private generateInput; + private generateHintToggler; + private generateHint; + private onClickHintToggler; + private onClickInput; +} diff --git a/input/select.ts b/input/select.ts new file mode 100644 index 0000000..1989c06 --- /dev/null +++ b/input/select.ts @@ -0,0 +1,171 @@ +import { WuiInputOption } from "./option.js" + +export interface WuiKeyValue { + [key: string]: string +} + +export interface WuiKeySelectOption { + [key: string]: WuiInputOption +} + +export interface WuiInputSelectOpts { + label: string + name: string + options: WuiKeySelectOption + id?: string + hint?: string + is_disabled?: boolean + onChangeHandler?: (key: string, value: string) => void +} + +const WUI_INPUT_SELECT_CLASS = "wui_input_select" +const WUI_INPUT_SELECT_CLASS_HINT = "wui_input_select_hint" +const WUI_INPUT_SELECT_CLASS_HINT_TOGGLER = "wui_input_select_hint_toggler" +const WUI_INPUT_SELECT_CLASS_INPUT = "wui_input_select_input" +const WUI_INPUT_SELECT_CLASS_LABEL = "wui_input_select_label" + +// +// WuiInputSelect create an HTML input for selecting one more item. +// +// Format of generated HTML output, +// +// <div [id=${id}] class="${WUI_INPUT_SELECT_CLASS}"> +// <div> +// <label class="${WUI_INPUT_SELECT_CLASS_LABEL}">${label}</label> +// <select +// name=${name} +// class="${WUI_INPUT_SELECT_CLASS_INPUT}" +// [disabled=${is_disabled}] +// > +// <option value="${options[key].value}">${key in options}</option> +// </select> +// [<span class="${WUI_INPUT_SELECT_CLASS_HINT_TOGGLER}">i </span>] +// </div> +// [<div class="${WUI_INPUT_SELECT_CLASS_HINT}">${hint}</div>] +// </div> +// +// The "hint" option is optional, if it set the input will have a hint toggler +// to display or hide the input information. +// +// User can set onChangeHandler to receive new value when the input value +// changes. +// +export class WuiInputSelect { + el: HTMLElement + private el_label!: HTMLElement + private el_input!: HTMLSelectElement + private el_hint!: HTMLElement + private el_hint_toggler!: HTMLElement + private value_key: WuiKeyValue = {} // The reverse of options.options + private value: string = "" + + constructor(public opts: WuiInputSelectOpts) { + this.el = document.createElement("div") + if (opts.id) { + this.el.id = opts.id + } + this.el.classList.add(WUI_INPUT_SELECT_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_SELECT_CLASS_LABEL) + this.el_label.innerHTML = `${this.opts.label} ` + wrapper.appendChild(this.el_label) + } + + private generateInput(wrapper: HTMLElement) { + this.el_input = document.createElement("select") + this.el_input.name = this.opts.name + this.el_input.classList.add(WUI_INPUT_SELECT_CLASS_INPUT) + + for (let key in this.opts.options) { + let option = this.opts.options[key] + + let el_option = document.createElement("option") + el_option.value = option.value + el_option.innerHTML = key + + if (option.selected) { + el_option.selected = true + } + + this.el_input.appendChild(el_option) + + this.value_key[option.value] = key + } + + if (this.opts.is_disabled) { + this.el_input.disabled = true + } + + if (this.opts.onChangeHandler) { + this.el_input.onclick = (ev: Event) => { + ev.preventDefault() + ev.stopPropagation() + this.onClickInput() + } + } + + wrapper.appendChild(this.el_input) + } + + private generateHintToggler(wrapper: HTMLElement) { + this.el_hint_toggler = document.createElement("span") + this.el_hint_toggler.classList.add(WUI_INPUT_SELECT_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() { + this.el_hint = document.createElement("div") + this.el_hint.classList.add(WUI_INPUT_SELECT_CLASS_HINT) + this.el_hint.innerHTML = this.opts.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 onClickInput() { + if (!this.opts.onChangeHandler) { + return false + } + + let value = this.el_input.value + let key = this.value_key[value] + if (this.value !== value) { + this.opts.onChangeHandler(key, value) + this.value = value + } + } +} |
