aboutsummaryrefslogtreecommitdiff
path: root/input/select.ts
diff options
context:
space:
mode:
Diffstat (limited to 'input/select.ts')
-rw-r--r--input/select.ts171
1 files changed, 171 insertions, 0 deletions
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 = " &#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() {
+ 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
+ }
+ }
+}