diff options
| author | Shulhan <ms@kilabit.info> | 2024-09-15 14:32:48 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2024-09-15 14:32:48 +0700 |
| commit | d1e96e09438b4a5c7580b86c469e817a61be991f (patch) | |
| tree | 4d81fb2fd62207bbb9162b81083c721ec8fd8e29 /input | |
| parent | 1cc9c9dd68a3a59c685505228336430624608852 (diff) | |
| download | pakakeh.ts-d1e96e09438b4a5c7580b86c469e817a61be991f.tar.xz | |
all: commit all generate JavaScript files
This is to simplify development on third party where they can
clone and include the file directly without installing or running
anything to build the files.
Diffstat (limited to 'input')
| -rw-r--r-- | input/checkboxes.js | 137 | ||||
| -rw-r--r-- | input/example.js | 224 | ||||
| -rw-r--r-- | input/file.js | 104 | ||||
| -rw-r--r-- | input/number.js | 170 | ||||
| -rw-r--r-- | input/option.js | 3 | ||||
| -rw-r--r-- | input/select.js | 152 | ||||
| -rw-r--r-- | input/string.js | 130 |
7 files changed, 920 insertions, 0 deletions
diff --git a/input/checkboxes.js b/input/checkboxes.js new file mode 100644 index 0000000..097f19c --- /dev/null +++ b/input/checkboxes.js @@ -0,0 +1,137 @@ +// SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info> +// SPDX-License-Identifier: GPL-3.0-or-later +const WUI_INPUT_CHECKBOXES_CLASS = "wui_input_checkboxes"; +const WUI_INPUT_CHECKBOXES_CLASS_HINT = "wui_input_checkboxes_hint"; +const WUI_INPUT_CHECKBOXES_CLASS_HINT_TOGGLER = "wui_input_checkboxes_hint_toggler"; +const WUI_INPUT_CHECKBOXES_CLASS_INPUT = "wui_input_checkboxes_input"; +const WUI_INPUT_CHECKBOXES_CLASS_LABEL = "wui_input_checkboxes_label"; +// +// WuiInputCheckboxes create an HTML input for selecting one or more item +// using checkbox. +// +// Format of generated HTML output, +// +// <div [id=${id}] class="${WUI_INPUT_CHECKBOXES_CLASS}"> +// <label class="${WUI_INPUT_CHECKBOXES_CLASS_LABEL}">${label}</label> +// [<span class="${WUI_INPUT_CHECKBOXES_CLASS_HINT_TOGGLER}">i </span>] +// <fieldset +// class="${WUI_INPUT_CHECKBOXES_CLASS_INPUT}" +// [disabled=${is_disabled}] +// > +// ${ for key in options } +// <div> +// <input name=${name} value="${options[key].value}"> +// <label>${key}</label> +// </div> +// ${ endfor } +// </fieldset> +// [<div class="${WUI_INPUT_CHECKBOXES_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. +// +// The onChangeHandler receive all checked values. +// +export class WuiInputCheckboxes { + constructor(opts) { + this.opts = opts; + this.values = []; + this.el = document.createElement("div"); + if (opts.id) { + this.el.id = opts.id; + } + this.el.classList.add(WUI_INPUT_CHECKBOXES_CLASS); + this.el.style.padding = "2px"; + this.generateLabel(this.el); + if (opts.hint) { + this.generateHintToggler(this.el); + } + this.generateInput(this.el); + if (opts.hint) { + this.generateHint(); + } + } + generateLabel(wrapper) { + this.elLabel = document.createElement("label"); + this.elLabel.classList.add(WUI_INPUT_CHECKBOXES_CLASS_LABEL); + this.elLabel.innerHTML = `${this.opts.label} `; + wrapper.appendChild(this.elLabel); + } + generateInput(wrapper) { + this.el_fieldset = document.createElement("fieldset"); + this.el_fieldset.classList.add(WUI_INPUT_CHECKBOXES_CLASS_INPUT); + Object.entries(this.opts.options).forEach(([key, option]) => { + const value = option.value; + const wrapper = document.createElement("div"); + const elCb = document.createElement("input"); + elCb.type = "checkbox"; + elCb.name = this.opts.name; + elCb.value = option.value; + if (option.selected) { + elCb.checked = true; + this.values.push(value); + } + elCb.onclick = () => { + this.onClickCheckbox(elCb.value, elCb.checked); + }; + wrapper.appendChild(elCb); + const elLabel = document.createElement("label"); + elLabel.innerHTML = key; + wrapper.appendChild(elLabel); + this.el_fieldset.appendChild(wrapper); + }); + if (this.opts.is_disabled) { + this.el_fieldset.disabled = true; + } + wrapper.appendChild(this.el_fieldset); + } + generateHintToggler(wrapper) { + this.el_hint_toggler = document.createElement("span"); + this.el_hint_toggler.classList.add(WUI_INPUT_CHECKBOXES_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); + } + generateHint() { + this.el_hint = document.createElement("div"); + this.el_hint.classList.add(WUI_INPUT_CHECKBOXES_CLASS_HINT); + this.el_hint.innerHTML = this.opts.hint || ""; + if (this.opts.is_hint_toggled) { + this.el_hint.style.display = "block"; + } + else { + this.el_hint.style.display = "none"; + } + this.el_hint.style.borderRadius = "2px"; + this.el_hint.style.padding = "4px"; + this.el_hint.style.marginTop = "2px"; + this.el.appendChild(this.el_hint); + } + onClickHintToggler() { + if (this.el_hint.style.display === "none") { + this.el_hint.style.display = "block"; + } + else { + this.el_hint.style.display = "none"; + } + } + onClickCheckbox(value, selected) { + for (let x = 0; x < this.values.length; x++) { + if (this.values[x] === value) { + this.values.splice(x, 1); + } + } + if (selected) { + this.values.push(value); + } + if (this.opts.onChangeHandler) { + this.opts.onChangeHandler(this.values); + } + } +} diff --git a/input/example.js b/input/example.js new file mode 100644 index 0000000..92abba9 --- /dev/null +++ b/input/example.js @@ -0,0 +1,224 @@ +// SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info> +// SPDX-License-Identifier: GPL-3.0-or-later +import { WuiInputFile } from "./file.js"; +import { WuiInputString } from "./string.js"; +import { WuiInputNumber } from "./number.js"; +import { WuiInputSelect } from "./select.js"; +import { WuiInputCheckboxes } from "./checkboxes.js"; +function exampleInputFile() { + const inputFile = new WuiInputFile(); + inputFile.label = "Input file"; + inputFile.hint = "Select file to be uploaded."; + inputFile.accept = "image/*"; + inputFile.onChange = (file) => { + console.log(`Uploading ${file.name} with size ${file.size}, type ${file.type}, and last modified at ${file.lastModified}.`); + }; + document.body.appendChild(inputFile.element()); +} +function exampleInputString() { + const elExample = document.createElement("div"); + const elTitle = document.createElement("h3"); + elTitle.innerText = "Input string"; + elExample.appendChild(elTitle); + const elOut = document.createElement("span"); + let opts = { + id: "my_input_string", + label: "Input string with ID", + value: "Hello, input string", + hint: "The input ID is 'my_input_string'", + onChangeHandler: (v) => { + elOut.innerText = v; + }, + }; + let elInputString = new WuiInputString(opts); + elExample.appendChild(elInputString.el); + opts = { + label: "Input string disabled", + value: "Hello, disabled input string", + is_disabled: true, + hint: "The input string is disabled", + is_hint_toggled: true, + onChangeHandler: (v) => { + elOut.innerText = v; + }, + }; + elInputString = new WuiInputString(opts); + elExample.appendChild(elInputString.el); + opts = { + label: "Input string without hint", + value: "Hello, input string without hint", + onChangeHandler: (v) => { + elOut.innerText = v; + }, + }; + elInputString = new WuiInputString(opts); + elExample.appendChild(elInputString.el); + const elOutLabel = document.createElement("div"); + elOutLabel.innerText = "Input string changes to "; + elOutLabel.appendChild(elOut); + elExample.appendChild(elOutLabel); + document.body.appendChild(elExample); +} +function exampleInputNumber() { + const elExample = document.createElement("div"); + const elTitle = document.createElement("h3"); + elTitle.innerText = "Input number"; + elExample.appendChild(elTitle); + const elOut = document.createElement("span"); + let opts = { + label: "Input number", + value: 1, + onChangeHandler: (val) => { + elOut.innerText = "" + val; + }, + }; + let inputNum = new WuiInputNumber(opts); + elExample.appendChild(inputNum.el); + opts = { + id: "my_input_number", + label: "Input number with ID", + value: 10, + hint: "The ID for this input is 'my_input_number'", + onChangeHandler: (val) => { + elOut.innerText = "" + val; + }, + }; + inputNum = new WuiInputNumber(opts); + elExample.appendChild(inputNum.el); + opts = { + label: "Input number disabled", + value: 1000, + hint: "Input number with 'is_disabled' set to true", + is_disabled: true, + is_hint_toggled: true, + onChangeHandler: (val) => { + elOut.innerText = "" + val; + }, + }; + inputNum = new WuiInputNumber(opts); + elExample.appendChild(inputNum.el); + opts = { + label: "Input number with hint", + value: 10000, + hint: "This is the <b>hint</b>", + onChangeHandler: (val) => { + elOut.innerText = "" + val; + }, + }; + inputNum = new WuiInputNumber(opts); + elExample.appendChild(inputNum.el); + opts = { + label: "Input number with max and min", + value: 10, + max: 12, + min: -20, + onChangeHandler: (val) => { + elOut.innerText = "" + val; + }, + }; + inputNum = new WuiInputNumber(opts); + elExample.appendChild(inputNum.el); + const elOutLabel = document.createElement("div"); + elOutLabel.innerText = "Input number changes to "; + elOutLabel.appendChild(elOut); + elExample.appendChild(elOutLabel); + document.body.appendChild(elExample); +} +function exampleInputSelect() { + const elExample = document.createElement("div"); + document.body.appendChild(elExample); + const elTitle = document.createElement("h3"); + elTitle.innerText = "Input select"; + elExample.appendChild(elTitle); + const elLog = document.createElement("div"); + const opts = { + 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 one of the option to see the price.", + is_hint_toggled: true, + onChangeHandler: (key, value) => { + elLog.innerText = `The select input changes to '${key}' with price '${value}'`; + }, + }; + const input = new WuiInputSelect(opts); + elExample.appendChild(input.el); + elExample.appendChild(elLog); +} +function exampleInputCheckboxes() { + const elExample = document.createElement("div"); + document.body.appendChild(elExample); + const elTitle = document.createElement("h3"); + elTitle.innerText = "Input checkboxes"; + elExample.appendChild(elTitle); + const elLog = document.createElement("div"); + let opts = { + name: "my_fruits", + label: "Input checkboxes", + options: { + mango: { + value: "1000", + selected: false, + }, + papaya: { + value: "200", + selected: false, + }, + rambutan: { + value: "100", + selected: true, + }, + }, + hint: "Select fruits.", + onChangeHandler: (values) => { + elLog.innerText = `You are selecting ${values}`; + }, + }; + let input = new WuiInputCheckboxes(opts); + elExample.appendChild(input.el); + opts = { + name: "my_fruits", + label: "Input checkboxes", + options: { + mango: { + value: "1000", + selected: false, + }, + papaya: { + value: "200", + selected: false, + }, + rambutan: { + value: "100", + selected: true, + }, + }, + hint: "Input select with 'is_disabled' and 'is_hint_toggled' is set to 'true'.", + is_disabled: true, + is_hint_toggled: true, + onChangeHandler: (values) => { + elLog.innerText = `You are selecting ${values}`; + }, + }; + input = new WuiInputCheckboxes(opts); + elExample.appendChild(input.el); + elExample.appendChild(elLog); +} +exampleInputFile(); +exampleInputString(); +exampleInputNumber(); +exampleInputSelect(); +exampleInputCheckboxes(); diff --git a/input/file.js b/input/file.js new file mode 100644 index 0000000..db33463 --- /dev/null +++ b/input/file.js @@ -0,0 +1,104 @@ +// SPDX-FileCopyrightText: 2024 M. Shulhan <ms@kilabit.info> +// SPDX-License-Identifier: GPL-3.0-or-later +export class WuiInputFile { + constructor() { + this.id = ""; + this.class = ""; + // Filter for files. For example "image/*" to allow selecting image files. + this.accept = ""; + this.label = ""; + this.hint = ""; + this.classInput = ""; // Custom CSS class for input element. + this.classLabel = ""; // Custom CSS class for label. + this.classHint = ""; // Custom CSS class for hint text. + this.classHintToggle = ""; // Custom CSS class for hint toggler. + this.isDisabled = false; + this.isHintShowed = false; + this.elHint = document.createElement("div"); + } + element() { + const el = document.createElement("div"); + if (this.id) { + el.id = this.id; + } + el.classList.add("wui_input_file"); + if (this.class) { + el.classList.add(this.class); + } + const wrapper = document.createElement("div"); + this.generateLabel(wrapper); + this.generateInput(wrapper); + this.generateHintToggler(wrapper); + el.appendChild(wrapper); + this.generateHint(el); + return el; + } + generateLabel(wrapper) { + const elLabel = document.createElement("label"); + elLabel.classList.add("wui_input_file_label"); + if (this.classLabel) { + elLabel.classList.add(this.classLabel); + } + elLabel.innerHTML = `${this.label} `; + wrapper.appendChild(elLabel); + } + generateInput(wrapper) { + const elInput = document.createElement("input"); + elInput.type = "file"; + elInput.classList.add("wui_input_file_input"); + if (this.classInput) { + elInput.classList.add(this.classInput); + } + if (this.accept) { + elInput.accept = this.accept; + } + if (this.isDisabled) { + elInput.disabled = true; + } + elInput.onchange = (event) => { + const file = event.target.files[0]; + if (file && this.onChange) { + this.onChange(file); + } + }; + wrapper.appendChild(elInput); + } + generateHintToggler(wrapper) { + const elHintToggler = document.createElement("span"); + elHintToggler.classList.add("wui_input_file_hint_toggler"); + if (this.classHintToggle) { + elHintToggler.classList.add(this.classHintToggle); + } + elHintToggler.innerHTML = " ℹ"; + elHintToggler.onmouseover = () => { + elHintToggler.style.cursor = "pointer"; + }; + elHintToggler.onclick = () => { + if (this.elHint.style.display === "none") { + this.elHint.style.display = "block"; + } + else { + this.elHint.style.display = "none"; + } + }; + wrapper.appendChild(elHintToggler); + } + generateHint(parent) { + this.elHint = document.createElement("div"); + this.elHint.classList.add("wui_input_file_hint"); + if (this.classHint) { + this.elHint.classList.add(this.classHint); + } + this.elHint.innerHTML = this.hint; + this.elHint.style.borderRadius = "2px"; + this.elHint.style.padding = "4px"; + this.elHint.style.marginTop = "2px"; + if (this.isHintShowed) { + this.elHint.style.display = "block"; + } + else { + this.elHint.style.display = "none"; + } + parent.appendChild(this.elHint); + } +} diff --git a/input/number.js b/input/number.js new file mode 100644 index 0000000..135066c --- /dev/null +++ b/input/number.js @@ -0,0 +1,170 @@ +// SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info> +// SPDX-License-Identifier: GPL-3.0-or-later +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} [${class_label}]"> +// ${label} | HTMLElement +// </label> +// <input +// class="${WUI_INPUT_NUMBER_CLASS_INPUT} [${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 { + constructor(opts) { + this.opts = opts; + this.value = 0; + 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"; + const 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(); + } + } + generateLabel(wrapper) { + this.el_label = document.createElement("label"); + this.el_label.classList.add(WUI_INPUT_NUMBER_CLASS_LABEL); + if (this.opts.class_label) { + this.el_label.classList.add(this.opts.class_label); + } + if (typeof this.opts.label === "string") { + this.el_label.innerHTML = `${this.opts.label} `; + } + else { + this.el_label.appendChild(this.opts.label); + } + wrapper.appendChild(this.el_label); + } + generateInput(wrapper) { + this.el_input = document.createElement("input"); + this.el_input.classList.add(WUI_INPUT_NUMBER_CLASS_INPUT); + if (this.opts.class_input) { + this.el_input.classList.add(this.opts.class_input); + } + this.el_input.type = "number"; + this.el_input.step = "any"; + 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) => { + return this.onKeyUp(ev); + }; + wrapper.appendChild(this.el_input); + } + generateHintToggler(wrapper) { + 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); + } + generateHint() { + const 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; + if (this.opts.is_hint_toggled) { + this.el_hint.style.display = "block"; + } + else { + this.el_hint.style.display = "none"; + } + this.el_hint.style.borderRadius = "2px"; + this.el_hint.style.padding = "4px"; + this.el_hint.style.marginTop = "2px"; + this.el.appendChild(this.el_hint); + } + onClickHintToggler() { + if (this.el_hint.style.display === "none") { + this.el_hint.style.display = "block"; + } + else { + this.el_hint.style.display = "none"; + } + } + onKeyUp(ev) { + const target = ev.target; + ev.preventDefault(); + const newValue = +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; + } + // set the input value. + set(v) { + this.el_input.value = "" + v; + } +} diff --git a/input/option.js b/input/option.js new file mode 100644 index 0000000..173bec3 --- /dev/null +++ b/input/option.js @@ -0,0 +1,3 @@ +// SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info> +// SPDX-License-Identifier: GPL-3.0-or-later +export {}; diff --git a/input/select.js b/input/select.js new file mode 100644 index 0000000..ba8b919 --- /dev/null +++ b/input/select.js @@ -0,0 +1,152 @@ +// SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info> +// SPDX-License-Identifier: GPL-3.0-or-later +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} [${class_label}]"> +// ${label} | HTMLElement +// </label> +// <select +// name=${name} +// class="${WUI_INPUT_SELECT_CLASS_INPUT} [${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 { + constructor(opts) { + this.opts = opts; + this.value_key = {}; // The reverse of options.options + this.value = ""; + 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"; + const 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(); + } + } + generateLabel(wrapper) { + this.el_label = document.createElement("label"); + this.el_label.classList.add(WUI_INPUT_SELECT_CLASS_LABEL); + if (this.opts.class_label) { + this.el_label.classList.add(this.opts.class_label); + } + if (typeof this.opts.label === "string") { + this.el_label.innerHTML = `${this.opts.label} `; + } + else { + this.el_label.appendChild(this.opts.label); + } + wrapper.appendChild(this.el_label); + } + generateInput(wrapper) { + this.el_input = document.createElement("select"); + this.el_input.name = this.opts.name; + this.el_input.classList.add(WUI_INPUT_SELECT_CLASS_INPUT); + if (this.opts.class_input) { + this.el_input.classList.add(this.opts.class_input); + } + Object.entries(this.opts.options).forEach(([key, option]) => { + const elOption = document.createElement("option"); + elOption.value = option.value; + elOption.innerHTML = key; + if (option.selected) { + elOption.selected = true; + } + this.el_input.appendChild(elOption); + 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) => { + ev.preventDefault(); + ev.stopPropagation(); + this.onClickInput(); + }; + } + wrapper.appendChild(this.el_input); + } + generateHintToggler(wrapper) { + 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); + } + generateHint() { + this.el_hint = document.createElement("div"); + this.el_hint.classList.add(WUI_INPUT_SELECT_CLASS_HINT); + this.el_hint.innerHTML = this.opts.hint || ""; + if (this.opts.is_hint_toggled) { + this.el_hint.style.display = "block"; + } + else { + this.el_hint.style.display = "none"; + } + this.el_hint.style.borderRadius = "2px"; + this.el_hint.style.padding = "4px"; + this.el_hint.style.marginTop = "2px"; + this.el.appendChild(this.el_hint); + } + onClickHintToggler() { + if (this.el_hint.style.display === "none") { + this.el_hint.style.display = "block"; + } + else { + this.el_hint.style.display = "none"; + } + } + onClickInput() { + if (!this.opts.onChangeHandler) { + return false; + } + const value = this.el_input.value; + if (this.value !== value) { + const key = this.value_key[value]; + if (key) { + this.opts.onChangeHandler(key, value); + this.value = value; + } + } + return true; + } + // set the input value. + set(v) { + this.el_input.value = v; + } +} diff --git a/input/string.js b/input/string.js new file mode 100644 index 0000000..437d66b --- /dev/null +++ b/input/string.js @@ -0,0 +1,130 @@ +// SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info> +// SPDX-License-Identifier: GPL-3.0-or-later +const WUI_INPUT_STRING_CLASS = "wui_input_string"; +const WUI_INPUT_STRING_CLASS_HINT = "wui_input_string_hint"; +const WUI_INPUT_STRING_CLASS_HINT_TOGGLER = "wui_input_string_hint_toggler"; +const WUI_INPUT_STRING_CLASS_INPUT = "wui_input_string_input"; +const WUI_INPUT_STRING_CLASS_LABEL = "wui_input_string_label"; +// WuiInputString create an HTML input for string with predefined options. +// The required options are "label" and "value". +// +// Format of generated HTML output, +// +// <div [id=${id}] class="${WUI_INPUT_STRING_CLASS}"> +// <div> +// <label class="${WUI_INPUT_STRING_CLASS_LABEL} [${class_label}]"> +// ${label} | HTMLElement +// </label> +// <input +// class="${WUI_INPUT_STRING_CLASS_INPUT} [${class_input}]" +// [disabled=${is_disabled}] +// value=${value} +// > +// [<span class="${WUI_INPUT_STRING_CLASS_HINT_TOGGLER}">i </span>] +// </div> +// [<div class="${WUI_INPUT_STRING_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 WuiInputString { + constructor(opts) { + this.opts = opts; + this.value = ""; + this.value = opts.value; + this.el = document.createElement("div"); + if (opts.id) { + this.el.id = opts.id; + } + this.el.classList.add(WUI_INPUT_STRING_CLASS); + this.el.style.padding = "2px"; + const 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(); + } + } + generateLabel(wrapper) { + this.el_label = document.createElement("label"); + this.el_label.classList.add(WUI_INPUT_STRING_CLASS_LABEL); + if (this.opts.class_label) { + this.el_label.classList.add(this.opts.class_label); + } + if (typeof this.opts.label === "string") { + this.el_label.innerHTML = `${this.opts.label} `; + } + else { + this.el_label.appendChild(this.opts.label); + } + wrapper.appendChild(this.el_label); + } + generateInput(wrapper) { + this.el_input = document.createElement("input"); + this.el_input.classList.add(WUI_INPUT_STRING_CLASS_INPUT); + if (this.opts.class_input) { + this.el_input.classList.add(this.opts.class_input); + } + this.el_input.value = "" + this.opts.value; + if (this.opts.is_disabled) { + this.el_input.disabled = true; + } + if (this.opts.onChangeHandler) { + this.el_input.onkeyup = () => { + if (this.opts.onChangeHandler) { + if (this.value !== this.el_input.value) { + this.opts.onChangeHandler(this.el_input.value); + this.value = this.el_input.value; + } + } + }; + } + wrapper.appendChild(this.el_input); + } + generateHintToggler(wrapper) { + this.el_hint_toggler = document.createElement("span"); + this.el_hint_toggler.classList.add(WUI_INPUT_STRING_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); + } + generateHint() { + this.el_hint = document.createElement("div"); + this.el_hint.classList.add(WUI_INPUT_STRING_CLASS_HINT); + this.el_hint.innerHTML = this.opts.hint || ""; + if (this.opts.is_hint_toggled) { + this.el_hint.style.display = "block"; + } + else { + this.el_hint.style.display = "none"; + } + this.el_hint.style.borderRadius = "2px"; + this.el_hint.style.padding = "4px"; + this.el_hint.style.marginTop = "2px"; + this.el.appendChild(this.el_hint); + } + onClickHintToggler() { + if (this.el_hint.style.display === "none") { + this.el_hint.style.display = "block"; + } + else { + this.el_hint.style.display = "none"; + } + } + // set the input value. + set(v) { + this.el_input.value = v; + } +} |
