diff options
| author | Shulhan <ms@kilabit.info> | 2023-10-23 02:20:07 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2023-10-24 22:27:14 +0700 |
| commit | 891a860299ac76739d59f46280cbed63ff07743e (patch) | |
| tree | dcf3ccf2c280b20ca1bf2692b794e1976cd34706 | |
| parent | a798d320563a1f9061a572e2b00537b38e34c4f0 (diff) | |
| download | pakakeh.ts-891a860299ac76739d59f46280cbed63ff07743e.tar.xz | |
all: reformat all files
I cannot remember how I reformat those files previously, probably without
any tools.
This changes reformat the TypeScript files using prettier and HTML files
using js-beautify.
| -rw-r--r-- | editor/editor.ts | 1153 | ||||
| -rw-r--r-- | editor/example.ts | 46 | ||||
| -rw-r--r-- | editor/index.html | 19 | ||||
| -rw-r--r-- | index.html | 23 | ||||
| -rw-r--r-- | input/checkboxes.ts | 239 | ||||
| -rw-r--r-- | input/example.ts | 404 | ||||
| -rw-r--r-- | input/index.html | 21 | ||||
| -rw-r--r-- | input/number.ts | 302 | ||||
| -rw-r--r-- | input/option.ts | 4 | ||||
| -rw-r--r-- | input/select.ts | 272 | ||||
| -rw-r--r-- | input/string.ts | 234 | ||||
| -rw-r--r-- | notif/example.ts | 68 | ||||
| -rw-r--r-- | notif/index.html | 38 | ||||
| -rw-r--r-- | notif/notif.ts | 78 | ||||
| -rw-r--r-- | response.ts | 8 | ||||
| -rw-r--r-- | tsconfig.json | 20 | ||||
| -rw-r--r-- | vfs/example.ts | 285 | ||||
| -rw-r--r-- | vfs/index.html | 19 | ||||
| -rw-r--r-- | vfs/vfs.ts | 304 | ||||
| -rw-r--r-- | websocket_client.ts | 260 |
20 files changed, 1903 insertions, 1894 deletions
diff --git a/editor/editor.ts b/editor/editor.ts index 7a4fda8..b8fd42a 100644 --- a/editor/editor.ts +++ b/editor/editor.ts @@ -1,409 +1,418 @@ // SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info> // SPDX-License-Identifier: GPL-3.0-or-later -import { WuiVfsNodeInterface } from "../vfs/vfs" +import { WuiVfsNodeInterface } from "../vfs/vfs"; -const WUI_EDITOR_CLASS = "wui_editor" -const WUI_EDITOR_CLASS_LINE = "wui_editor_line" -const WUI_EDITOR_CLASS_LINE_NUMBER = "wui_editor_line_number" -const WUI_EDITOR_CLASS_LINE_TEXT = "wui_editor_line_text" +const WUI_EDITOR_CLASS = "wui_editor"; +const WUI_EDITOR_CLASS_LINE = "wui_editor_line"; +const WUI_EDITOR_CLASS_LINE_NUMBER = "wui_editor_line_number"; +const WUI_EDITOR_CLASS_LINE_TEXT = "wui_editor_line_text"; export interface WuiEditorOptions { - id: string - is_editable: boolean + id: string; + is_editable: boolean; - // Handler that will be called when user select lines. - OnSelection(begin: number, end: number): void + // Handler that will be called when user select lines. + OnSelection(begin: number, end: number): void; - // Handler that will be called when user press CTRL+S. - OnSave(content: string): void + // Handler that will be called when user press CTRL+S. + OnSave(content: string): void; } export class WuiEditor { - id: string - is_editable: boolean - lines: WuiEditorLine[] = [] - private el!: HTMLElement - private sel!: Selection - private active_file: WuiVfsNodeInterface | null = null - private active_text: HTMLElement | null = null - private range_begin: number = -1 - private range_end: number = -1 - private raw_lines: string[] = [] - private range!: Range - private is_key_control: boolean = false - private unre: WuiEditorUndoRedo = new WuiEditorUndoRedo() + id: string; + is_editable: boolean; + lines: WuiEditorLine[] = []; + private el!: HTMLElement; + private sel!: Selection; + private active_file: WuiVfsNodeInterface | null = null; + private active_text: HTMLElement | null = null; + private range_begin: number = -1; + private range_end: number = -1; + private raw_lines: string[] = []; + private range!: Range; + private is_key_control: boolean = false; + private unre: WuiEditorUndoRedo = new WuiEditorUndoRedo(); - constructor(public opts: WuiEditorOptions) { - this.id = opts.id - this.is_editable = opts.is_editable + constructor(public opts: WuiEditorOptions) { + this.id = opts.id; + this.is_editable = opts.is_editable; - let el = document.getElementById(opts.id) - if (!el) { - console.error("WuiEditor: element ID not found:", opts.id) - return - } - this.el = el + let el = document.getElementById(opts.id); + if (!el) { + console.error("WuiEditor: element ID not found:", opts.id); + return; + } + this.el = el; - this.initStyle() + this.initStyle(); - this.el.classList.add(WUI_EDITOR_CLASS) + this.el.classList.add(WUI_EDITOR_CLASS); - let sel = window.getSelection() - if (!sel) { - console.error("WuiEditor: cannot get window selection", opts.id) - return - } - this.sel = sel - this.range = document.createRange() + let sel = window.getSelection(); + if (!sel) { + console.error("WuiEditor: cannot get window selection", opts.id); + return; + } + this.sel = sel; + this.range = document.createRange(); - document.onkeyup = (ev: KeyboardEvent) => { - this.onKeyupDocument(this, ev) - } - } + document.onkeyup = (ev: KeyboardEvent) => { + this.onKeyupDocument(this, ev); + }; + } - // GetContent return content of file. - GetContent(): string { - let content = "" - for (let x = 0; x < this.lines.length; x++) { - if (x > 0) { - content += "\n" - } - content += this.lines[x].el_text.innerText - } - return content - } + // GetContent return content of file. + GetContent(): string { + let content = ""; + for (let x = 0; x < this.lines.length; x++) { + if (x > 0) { + content += "\n"; + } + content += this.lines[x].el_text.innerText; + } + return content; + } - GetSelectionRange(): WuiEditorSelectionRangeInterface { - return { - begin_at: this.range_begin, - end_at: this.range_end, - } as WuiEditorSelectionRangeInterface - } + GetSelectionRange(): WuiEditorSelectionRangeInterface { + return { + begin_at: this.range_begin, + end_at: this.range_end, + } as WuiEditorSelectionRangeInterface; + } - OnClickText(text: HTMLElement) { - let sel = window.getSelection() - if (sel) { - this.sel = sel - } - } + OnClickText(text: HTMLElement) { + let sel = window.getSelection(); + if (sel) { + this.sel = sel; + } + } - OnKeyup(x: number, text: HTMLElement, ev: KeyboardEvent) { - let text_before: string - let text_after: string - let off: number + OnKeyup(x: number, text: HTMLElement, ev: KeyboardEvent) { + let text_before: string; + let text_after: string; + let off: number; - switch (ev.key) { - case "Alt": - case "ArrowDown": - case "ArrowLeft": - case "ArrowRight": - case "ArrowUp": - case "CapsLock": - case "ContextMenu": - case "End": - case "Home": - case "Insert": - case "OS": - case "PageDown": - case "PageUp": - case "Pause": - case "PrintScreen": - case "ScrollLock": - case "Shift": - break + switch (ev.key) { + case "Alt": + case "ArrowDown": + case "ArrowLeft": + case "ArrowRight": + case "ArrowUp": + case "CapsLock": + case "ContextMenu": + case "End": + case "Home": + case "Insert": + case "OS": + case "PageDown": + case "PageUp": + case "Pause": + case "PrintScreen": + case "ScrollLock": + case "Shift": + break; - case "Backspace": - ev.preventDefault() + case "Backspace": + ev.preventDefault(); - text_before = this.raw_lines[x] - let el_text_curr = this.lines[x].el_text - text_after = el_text_curr.innerText + text_before = this.raw_lines[x]; + let el_text_curr = this.lines[x].el_text; + text_after = el_text_curr.innerText; - off = this.sel.focusOffset - if (off > 0) { - this.unre.DoUpdate(x, text_before, text_after) + off = this.sel.focusOffset; + if (off > 0) { + this.unre.DoUpdate(x, text_before, text_after); - this.raw_lines[x] = text_after - this.setCaret(el_text_curr, off) - return false - } + this.raw_lines[x] = text_after; + this.setCaret(el_text_curr, off); + return false; + } - // Join current line with previous. - let el_text_prev = this.lines[x - 1].el_text + // Join current line with previous. + let el_text_prev = this.lines[x - 1].el_text; - this.unre.DoJoin(x - 1, el_text_prev.innerText, el_text_curr.innerText) + this.unre.DoJoin(x - 1, el_text_prev.innerText, el_text_curr.innerText); - off = el_text_prev.innerText.length - el_text_prev.innerText = el_text_prev.innerText + el_text_curr.innerText - this.raw_lines[x - 1] = el_text_prev.innerText + off = el_text_prev.innerText.length; + el_text_prev.innerText = + el_text_prev.innerText + el_text_curr.innerText; + this.raw_lines[x - 1] = el_text_prev.innerText; - // Remove the current line - this.deleteLine(x) - this.setCaret(el_text_prev, off) - return false + // Remove the current line + this.deleteLine(x); + this.setCaret(el_text_prev, off); + return false; - case "Control": - this.is_key_control = false - break + case "Control": + this.is_key_control = false; + break; - case "Enter": - ev.preventDefault() - break + case "Enter": + ev.preventDefault(); + break; - case "r": - if (this.is_key_control) { - ev.preventDefault() - return - } - break + case "r": + if (this.is_key_control) { + ev.preventDefault(); + return; + } + break; - case "z": - if (this.is_key_control) { - ev.preventDefault() - return - } - break + case "z": + if (this.is_key_control) { + ev.preventDefault(); + return; + } + break; - default: - if (this.is_key_control) { - break - } - this.unre.DoUpdate(x, this.raw_lines[x], this.lines[x].el_text.innerText) - this.raw_lines[x] = this.lines[x].el_text.innerText - } - return true - } + default: + if (this.is_key_control) { + break; + } + this.unre.DoUpdate( + x, + this.raw_lines[x], + this.lines[x].el_text.innerText, + ); + this.raw_lines[x] = this.lines[x].el_text.innerText; + } + return true; + } - OnKeydownOnLine(x: number, el_text: HTMLElement, ev: KeyboardEvent) { - let text_before: string - let text_after: string - let off: number + OnKeydownOnLine(x: number, el_text: HTMLElement, ev: KeyboardEvent) { + let text_before: string; + let text_after: string; + let off: number; - switch (ev.key) { - case "ArrowUp": - if (x == 0) { - return false - } - ev.preventDefault() + switch (ev.key) { + case "ArrowUp": + if (x == 0) { + return false; + } + ev.preventDefault(); - let el_text = this.lines[x - 1].el_text - let off = this.sel.focusOffset - if (off > el_text.innerText.length) { - off = el_text.innerText.length - } - this.setCaret(el_text, off) + let el_text = this.lines[x - 1].el_text; + let off = this.sel.focusOffset; + if (off > el_text.innerText.length) { + off = el_text.innerText.length; + } + this.setCaret(el_text, off); - if (x == 1) { - this.el.scrollTop = 0 - } else if (x * 23 < this.el.scrollTop) { - this.el.scrollTop -= 25 - } - return false + if (x == 1) { + this.el.scrollTop = 0; + } else if (x * 23 < this.el.scrollTop) { + this.el.scrollTop -= 25; + } + return false; - case "ArrowDown": - if (x == this.lines.length - 1) { - return false - } - ev.preventDefault() + case "ArrowDown": + if (x == this.lines.length - 1) { + return false; + } + ev.preventDefault(); - el_text = this.lines[x + 1].el_text - off = this.sel.focusOffset - if (off > el_text.innerText.length) { - off = el_text.innerText.length - } - this.setCaret(el_text, off) + el_text = this.lines[x + 1].el_text; + off = this.sel.focusOffset; + if (off > el_text.innerText.length) { + off = el_text.innerText.length; + } + this.setCaret(el_text, off); - x += 2 - if (x * 25 >= this.el.clientHeight + this.el.scrollTop) { - this.el.scrollTop += 25 - } - return false + x += 2; + if (x * 25 >= this.el.clientHeight + this.el.scrollTop) { + this.el.scrollTop += 25; + } + return false; - case "Control": - this.is_key_control = true - break + case "Control": + this.is_key_control = true; + break; - case "Delete": - ev.preventDefault() + case "Delete": + ev.preventDefault(); - let is_join_line_after = false - let el_text_current = this.lines[x].el_text + let is_join_line_after = false; + let el_text_current = this.lines[x].el_text; - off = this.sel.focusOffset - text_before = el_text_current.innerText - text_after = "" + off = this.sel.focusOffset; + text_before = el_text_current.innerText; + text_after = ""; - if (text_before.length === 0 || off === text_before.length) { - // Current line is empty, join the next line to current - // line; or - // Current offset is at the end of text, join the next - // line to current line. - is_join_line_after = true - } + if (text_before.length === 0 || off === text_before.length) { + // Current line is empty, join the next line to current + // line; or + // Current offset is at the end of text, join the next + // line to current line. + is_join_line_after = true; + } - if (is_join_line_after) { - if (x+1 < this.lines.length) { - let el_text_after = this.lines[x+1].el_text - text_after = el_text_after.innerText - el_text_after.innerText = "" + if (is_join_line_after) { + if (x + 1 < this.lines.length) { + let el_text_after = this.lines[x + 1].el_text; + text_after = el_text_after.innerText; + el_text_after.innerText = ""; - this.unre.DoJoin(x, text_before, text_after) + this.unre.DoJoin(x, text_before, text_after); - this.deleteLine(x+1) - text_after = text_before + text_after - } - } else { - text_after = text_before.slice(0, off) + text_before.slice(off+1, text_before.length) + this.deleteLine(x + 1); + text_after = text_before + text_after; + } + } else { + text_after = + text_before.slice(0, off) + + text_before.slice(off + 1, text_before.length); - this.unre.DoUpdate(x, text_before, text_after) - } + this.unre.DoUpdate(x, text_before, text_after); + } - this.lines[x].el_text.innerText = text_after - this.raw_lines[x] = text_after - this.setCaret(el_text_current, off) - break + this.lines[x].el_text.innerText = text_after; + this.raw_lines[x] = text_after; + this.setCaret(el_text_current, off); + break; - case "Enter": - ev.preventDefault() + case "Enter": + ev.preventDefault(); - off = this.sel.focusOffset - let text = this.lines[x].el_text.innerText - text_before = text.slice(0, off) - text_after = text.slice(off, text.length) + off = this.sel.focusOffset; + let text = this.lines[x].el_text.innerText; + text_before = text.slice(0, off); + text_after = text.slice(off, text.length); - this.unre.DoSplit(x, text_before, text_after) + this.unre.DoSplit(x, text_before, text_after); - this.lines[x].el_text.innerText = text_before - this.raw_lines[x] = text_before + this.lines[x].el_text.innerText = text_before; + this.raw_lines[x] = text_before; - this.insertNewline(x + 1, text_after) - if (x + 3 >= this.raw_lines.length) { - this.el.scrollTop = this.el.scrollHeight - } - break + this.insertNewline(x + 1, text_after); + if (x + 3 >= this.raw_lines.length) { + this.el.scrollTop = this.el.scrollHeight; + } + break; - case "Tab": - ev.preventDefault() + case "Tab": + ev.preventDefault(); - el_text = this.lines[x].el_text - off = this.sel.focusOffset - text_before = el_text.innerText - text_after = - text_before.slice(0, off) + "\t" + text_before.slice(off, text_before.length) + el_text = this.lines[x].el_text; + off = this.sel.focusOffset; + text_before = el_text.innerText; + text_after = + text_before.slice(0, off) + + "\t" + + text_before.slice(off, text_before.length); - this.unre.DoUpdate(x, text_before, text_after) - el_text.innerText = text_after - this.raw_lines[x] = text_after + this.unre.DoUpdate(x, text_before, text_after); + el_text.innerText = text_after; + this.raw_lines[x] = text_after; - this.setCaret(el_text, off + 1) - break + this.setCaret(el_text, off + 1); + break; - case "r": - if (this.is_key_control) { - ev.preventDefault() - this.doRedo() - return - } - break + case "r": + if (this.is_key_control) { + ev.preventDefault(); + this.doRedo(); + return; + } + break; - case "s": - if (this.is_key_control) { - ev.preventDefault() - ev.stopPropagation() - if (this.opts.OnSave) { - this.opts.OnSave(this.GetContent()) - } - return false - } - break + case "s": + if (this.is_key_control) { + ev.preventDefault(); + ev.stopPropagation(); + if (this.opts.OnSave) { + this.opts.OnSave(this.GetContent()); + } + return false; + } + break; - case "z": - if (this.is_key_control) { - ev.preventDefault() - this.doUndo() - return - } - break - } - } + case "z": + if (this.is_key_control) { + ev.preventDefault(); + this.doUndo(); + return; + } + break; + } + } - OnMouseDownAtLine(x: number) { - this.range_begin = x - } + OnMouseDownAtLine(x: number) { + this.range_begin = x; + } - OnMouseUpAtLine(x: number) { - this.range_end = x - if (this.range_end < this.range_begin) { - return - } - let y = 0 - for (; y < this.range_begin; y++) { - this.el.children[y].setAttribute("style", "") - } - for (; y <= this.range_end; y++) { - this.el.children[y].setAttribute("style", "background-color:lightsalmon") - } - for (; y < this.el.children.length; y++) { - this.el.children[y].setAttribute("style", "") - } - if (this.opts.OnSelection) { - this.opts.OnSelection(this.range_begin, this.range_end) - } - } + OnMouseUpAtLine(x: number) { + this.range_end = x; + if (this.range_end < this.range_begin) { + return; + } + let y = 0; + for (; y < this.range_begin; y++) { + this.el.children[y].setAttribute("style", ""); + } + for (; y <= this.range_end; y++) { + this.el.children[y].setAttribute("style", "background-color:lightsalmon"); + } + for (; y < this.el.children.length; y++) { + this.el.children[y].setAttribute("style", ""); + } + if (this.opts.OnSelection) { + this.opts.OnSelection(this.range_begin, this.range_end); + } + } - // - // SetEditOff make the content not editable. - // - SetEditOff() { - for (let x = 0; x < this.lines.length; x++) { - this.lines[x].SetEditOff() - } - } + // + // SetEditOff make the content not editable. + // + SetEditOff() { + for (let x = 0; x < this.lines.length; x++) { + this.lines[x].SetEditOff(); + } + } - // - // SetEditOn make the content to be editable. - // - SetEditOn() { - for (let x = 0; x < this.lines.length; x++) { - this.lines[x].SetEditOn() - } - } + // + // SetEditOn make the content to be editable. + // + SetEditOn() { + for (let x = 0; x < this.lines.length; x++) { + this.lines[x].SetEditOn(); + } + } - // Open the node for editing. - // The content MUST be encoded in base64. - Open(node: WuiVfsNodeInterface): void { - this.active_file = node + // Open the node for editing. + // The content MUST be encoded in base64. + Open(node: WuiVfsNodeInterface): void { + this.active_file = node; - let content = atob(node.content) - content = content.replace("\r\n", "\n") - this.raw_lines = content.split("\n") + let content = atob(node.content); + content = content.replace("\r\n", "\n"); + this.raw_lines = content.split("\n"); - this.lines = [] - for (let x = 0; x < this.raw_lines.length; x++) { - let line = new WuiEditorLine(x, this.raw_lines[x], this) - this.lines.push(line) - } + this.lines = []; + for (let x = 0; x < this.raw_lines.length; x++) { + let line = new WuiEditorLine(x, this.raw_lines[x], this); + this.lines.push(line); + } - this.render() - } + this.render(); + } - // ClearSelection clear selection range indicator. - ClearSelection() { - if (this.range_begin < 0 || this.range_end == 0) { - return - } - for (let x = this.range_begin; x <= this.range_end; x++) { - this.el.children[x].setAttribute("style", "") - } - this.range_begin = -1 - this.range_end = -1 - } + // ClearSelection clear selection range indicator. + ClearSelection() { + if (this.range_begin < 0 || this.range_end == 0) { + return; + } + for (let x = this.range_begin; x <= this.range_end; x++) { + this.el.children[x].setAttribute("style", ""); + } + this.range_begin = -1; + this.range_end = -1; + } - private initStyle() { - let style = document.createElement("style") - style.type = "text/css" - style.innerText = ` + private initStyle() { + let style = document.createElement("style"); + style.type = "text/css"; + style.innerText = ` [contenteditable] { outline: 0px solid transparent; } @@ -439,274 +448,278 @@ export class WuiEditor { white-space: pre-wrap; width: calc(100% - 60px); } - ` - document.head.appendChild(style) - } + `; + document.head.appendChild(style); + } - private doJoin(changes: WuiEditorActionChangesInterface) { - this.lines[changes.curr_line].el_text.innerText = changes.curr_text - this.deleteLine(changes.next_line) - this.setCaret(this.lines[changes.curr_line].el_text, 0) - } + private doJoin(changes: WuiEditorActionChangesInterface) { + this.lines[changes.curr_line].el_text.innerText = changes.curr_text; + this.deleteLine(changes.next_line); + this.setCaret(this.lines[changes.curr_line].el_text, 0); + } - private doSplit(changes: WuiEditorActionChangesInterface) { - this.lines[changes.curr_line].el_text.innerText = changes.curr_text - this.insertNewline(changes.next_line, changes.next_text) - } + private doSplit(changes: WuiEditorActionChangesInterface) { + this.lines[changes.curr_line].el_text.innerText = changes.curr_text; + this.insertNewline(changes.next_line, changes.next_text); + } - private doUpdate(changes: WuiEditorActionChangesInterface) { - this.lines[changes.curr_line].el_text.innerText = changes.curr_text - this.setCaret(this.lines[changes.curr_line].el_text, 0) - } + private doUpdate(changes: WuiEditorActionChangesInterface) { + this.lines[changes.curr_line].el_text.innerText = changes.curr_text; + this.setCaret(this.lines[changes.curr_line].el_text, 0); + } - private doRedo() { - const act = this.unre.Redo() - if (!act) { - return - } - switch (act.kind) { - case "join": - this.doJoin(act.after) - break - case "split": - this.doSplit(act.after) - break - case "update": - this.doUpdate(act.after) - break - } - } + private doRedo() { + const act = this.unre.Redo(); + if (!act) { + return; + } + switch (act.kind) { + case "join": + this.doJoin(act.after); + break; + case "split": + this.doSplit(act.after); + break; + case "update": + this.doUpdate(act.after); + break; + } + } - private doUndo() { - const act = this.unre.Undo() - if (!act) { - return - } - switch (act.kind) { - case "join": - this.doSplit(act.before) - break - case "split": - this.doJoin(act.before) - break - case "update": - this.doUpdate(act.before) - break - } - } + private doUndo() { + const act = this.unre.Undo(); + if (!act) { + return; + } + switch (act.kind) { + case "join": + this.doSplit(act.before); + break; + case "split": + this.doJoin(act.before); + break; + case "update": + this.doUpdate(act.before); + break; + } + } - private deleteLine(x: number) { - this.lines.splice(x, 1) - this.raw_lines.splice(x, 1) + private deleteLine(x: number) { + this.lines.splice(x, 1); + this.raw_lines.splice(x, 1); - // Reset the line numbers. - for (; x < this.lines.length; x++) { - this.lines[x].SetNumber(x) - } - this.render() - } + // Reset the line numbers. + for (; x < this.lines.length; x++) { + this.lines[x].SetNumber(x); + } + this.render(); + } - private insertNewline(x: number, text: string) { - let newline = new WuiEditorLine(x, text, this) - for (let y = x; y < this.lines.length; y++) { - this.lines[y].SetNumber(y + 1) - } + private insertNewline(x: number, text: string) { + let newline = new WuiEditorLine(x, text, this); + for (let y = x; y < this.lines.length; y++) { + this.lines[y].SetNumber(y + 1); + } - this.lines.splice(x, 0, newline) - this.raw_lines.splice(x, 0, text) + this.lines.splice(x, 0, newline); + this.raw_lines.splice(x, 0, text); - this.render() - this.setCaret(newline.el_text, 0) - } + this.render(); + this.setCaret(newline.el_text, 0); + } - private onKeyupDocument(ed: WuiEditor, ev: KeyboardEvent) { - switch (ev.key) { - case "Escape": - ev.preventDefault() - ed.ClearSelection() - break - } - return true - } + private onKeyupDocument(ed: WuiEditor, ev: KeyboardEvent) { + switch (ev.key) { + case "Escape": + ev.preventDefault(); + ed.ClearSelection(); + break; + } + return true; + } - private render() { - this.el.innerHTML = "" - for (const line of this.lines) { - this.el.appendChild(line.el) - } - } + private render() { + this.el.innerHTML = ""; + for (const line of this.lines) { + this.el.appendChild(line.el); + } + } - private setCaret(el_text: HTMLElement, off: number) { - if (el_text.firstChild) { - this.range.setStart(el_text.firstChild, off) - } else { - this.range.setStart(el_text, off) - } - this.range.collapse(true) - this.sel.removeAllRanges() - this.sel.addRange(this.range) - } + private setCaret(el_text: HTMLElement, off: number) { + if (el_text.firstChild) { + this.range.setStart(el_text.firstChild, off); + } else { + this.range.setStart(el_text, off); + } + this.range.collapse(true); + this.sel.removeAllRanges(); + this.sel.addRange(this.range); + } } class WuiEditorLine { - private line_num: number = 0 - el!: HTMLElement - el_number!: HTMLElement - el_text!: HTMLElement + private line_num: number = 0; + el!: HTMLElement; + el_number!: HTMLElement; + el_text!: HTMLElement; - constructor(public x: number, public text: string, ed: WuiEditor) { - this.line_num = x - this.el = document.createElement("div") - this.el.classList.add(WUI_EDITOR_CLASS_LINE) + constructor( + public x: number, + public text: string, + ed: WuiEditor, + ) { + this.line_num = x; + this.el = document.createElement("div"); + this.el.classList.add(WUI_EDITOR_CLASS_LINE); - this.el_number = document.createElement("span") - this.el_number.classList.add(WUI_EDITOR_CLASS_LINE_NUMBER) - this.el_number.innerText = this.line_num + 1 + "" + this.el_number = document.createElement("span"); + this.el_number.classList.add(WUI_EDITOR_CLASS_LINE_NUMBER); + this.el_number.innerText = this.line_num + 1 + ""; - this.el_number.onmousedown = (ev: MouseEvent) => { - ed.OnMouseDownAtLine(this.line_num) - } - this.el_number.onmouseup = (ev: MouseEvent) => { - ed.OnMouseUpAtLine(this.line_num) - } + this.el_number.onmousedown = (ev: MouseEvent) => { + ed.OnMouseDownAtLine(this.line_num); + }; + this.el_number.onmouseup = (ev: MouseEvent) => { + ed.OnMouseUpAtLine(this.line_num); + }; - this.el_text = document.createElement("span") - this.el_text.classList.add(WUI_EDITOR_CLASS_LINE_TEXT) - this.el_text.innerText = text - this.el_text.contentEditable = "true" + this.el_text = document.createElement("span"); + this.el_text.classList.add(WUI_EDITOR_CLASS_LINE_TEXT); + this.el_text.innerText = text; + this.el_text.contentEditable = "true"; - this.el_text.onclick = (ev: MouseEvent) => { - ed.OnClickText(this.el_text) - } + this.el_text.onclick = (ev: MouseEvent) => { + ed.OnClickText(this.el_text); + }; - this.el_text.onkeydown = (ev: KeyboardEvent) => { - return ed.OnKeydownOnLine(this.line_num, this.el_text, ev) - } - this.el_text.onkeyup = (ev: KeyboardEvent) => { - return ed.OnKeyup(this.line_num, this.el_text, ev) - } + this.el_text.onkeydown = (ev: KeyboardEvent) => { + return ed.OnKeydownOnLine(this.line_num, this.el_text, ev); + }; + this.el_text.onkeyup = (ev: KeyboardEvent) => { + return ed.OnKeyup(this.line_num, this.el_text, ev); + }; - this.el_text.addEventListener("paste", (ev: ClipboardEvent) => { - if (!ev.clipboardData) { - return - } - ev.preventDefault() - const text = ev.clipboardData.getData("text/plain") - document.execCommand("insertHTML", false, text) - }) + this.el_text.addEventListener("paste", (ev: ClipboardEvent) => { + if (!ev.clipboardData) { + return; + } + ev.preventDefault(); + const text = ev.clipboardData.getData("text/plain"); + document.execCommand("insertHTML", false, text); + }); - this.el.appendChild(this.el_number) - this.el.appendChild(this.el_text) - } + this.el.appendChild(this.el_number); + this.el.appendChild(this.el_text); + } - SetNumber(x: number) { - this.line_num = x - this.el_number.innerText = x + 1 + "" - } + SetNumber(x: number) { + this.line_num = x; + this.el_number.innerText = x + 1 + ""; + } - SetEditOn() { - this.el_text.contentEditable = "true" - } + SetEditOn() { + this.el_text.contentEditable = "true"; + } - SetEditOff() { - this.el_text.contentEditable = "false" - } + SetEditOff() { + this.el_text.contentEditable = "false"; + } } // // WuiEditorUndoRedo store the state of actions. // class WuiEditorUndoRedo { - private idx: number = 0 - private actions: WuiEditorActionInterface[] = [] + private idx: number = 0; + private actions: WuiEditorActionInterface[] = []; - DoJoin(prevLine: number, prevText: string, curr_text: string) { - let curr_line = prevLine + 1 - let action: WuiEditorActionInterface = { - kind: "join", - before: { - curr_line: prevLine, - curr_text: prevText, - next_line: prevLine + 1, - next_text: curr_text, - }, - after: { - curr_line: prevLine, - curr_text: prevText + curr_text, - next_line: prevLine + 1, - next_text: "", - }, - } - if (this.actions.length > 0) { - this.actions = this.actions.slice(0, this.idx) - } - this.actions.push(action) - this.idx++ - } + DoJoin(prevLine: number, prevText: string, curr_text: string) { + let curr_line = prevLine + 1; + let action: WuiEditorActionInterface = { + kind: "join", + before: { + curr_line: prevLine, + curr_text: prevText, + next_line: prevLine + 1, + next_text: curr_text, + }, + after: { + curr_line: prevLine, + curr_text: prevText + curr_text, + next_line: prevLine + 1, + next_text: "", + }, + }; + if (this.actions.length > 0) { + this.actions = this.actions.slice(0, this.idx); + } + this.actions.push(action); + this.idx++; + } - DoSplit(curr_line: number, curr_text: string, next_text: string) { - let action = { - kind: "split", - before: { - curr_line: curr_line, - curr_text: curr_text + next_text, - next_line: curr_line + 1, - next_text: "", - }, - after: { - curr_line: curr_line, - curr_text: curr_text, - next_line: curr_line + 1, - next_text: next_text, - }, - } - if (this.actions.length > 0) { - this.actions = this.actions.slice(0, this.idx) - } - this.actions.push(action) - this.idx++ - } + DoSplit(curr_line: number, curr_text: string, next_text: string) { + let action = { + kind: "split", + before: { + curr_line: curr_line, + curr_text: curr_text + next_text, + next_line: curr_line + 1, + next_text: "", + }, + after: { + curr_line: curr_line, + curr_text: curr_text, + next_line: curr_line + 1, + next_text: next_text, + }, + }; + if (this.actions.length > 0) { + this.actions = this.actions.slice(0, this.idx); + } + this.actions.push(action); + this.idx++; + } - DoUpdate(line_num: number, text_before: string, text_after: string) { - const action: WuiEditorActionInterface = { - kind: "update", - before: { - curr_line: line_num, - curr_text: text_before, - next_line: 0, - next_text: "", - }, - after: { - curr_line: line_num, - curr_text: text_after, - next_line: 0, - next_text: "", - }, - } + DoUpdate(line_num: number, text_before: string, text_after: string) { + const action: WuiEditorActionInterface = { + kind: "update", + before: { + curr_line: line_num, + curr_text: text_before, + next_line: 0, + next_text: "", + }, + after: { + curr_line: line_num, + curr_text: text_after, + next_line: 0, + next_text: "", + }, + }; - if (this.actions.length > 0) { - this.actions = this.actions.slice(0, this.idx) - } - this.actions.push(action) - this.idx++ - } + if (this.actions.length > 0) { + this.actions = this.actions.slice(0, this.idx); + } + this.actions.push(action); + this.idx++; + } - Undo(): WuiEditorActionInterface | null { - if (this.idx == 0) { - return null - } - this.idx-- - return this.actions[this.idx] - } + Undo(): WuiEditorActionInterface | null { + if (this.idx == 0) { + return null; + } + this.idx--; + return this.actions[this.idx]; + } - Redo(): WuiEditorActionInterface | null { - if (this.idx == this.actions.length) { - return null - } - let action = this.actions[this.idx] - this.idx++ - return action - } + Redo(): WuiEditorActionInterface | null { + if (this.idx == this.actions.length) { + return null; + } + let action = this.actions[this.idx]; + this.idx++; + return action; + } } // There are three kind of action @@ -716,19 +729,19 @@ class WuiEditorUndoRedo { // * join: join line using backspace. // interface WuiEditorActionInterface { - kind: string - before: WuiEditorActionChangesInterface - after: WuiEditorActionChangesInterface + kind: string; + before: WuiEditorActionChangesInterface; + after: WuiEditorActionChangesInterface; } interface WuiEditorActionChangesInterface { - curr_line: number - curr_text: string - next_line: number - next_text: string + curr_line: number; + curr_text: string; + next_line: number; + next_text: string; } interface WuiEditorSelectionRangeInterface { - begin_at: number - end_at: number + begin_at: number; + end_at: number; } diff --git a/editor/example.ts b/editor/example.ts index 4cca24d..19bc0b9 100644 --- a/editor/example.ts +++ b/editor/example.ts @@ -1,19 +1,19 @@ // SPDX-FileCopyrightText: 2019 M. Shulhan <ms@kilabit.info> // SPDX-License-Identifier: GPL-3.0-or-later -import { WuiEditor, WuiEditorOptions } from "./editor.js" -import { WuiVfsNodeInterface } from "../vfs/vfs.js" +import { WuiEditor, WuiEditorOptions } from "./editor.js"; +import { WuiVfsNodeInterface } from "../vfs/vfs.js"; let nodeFile: WuiVfsNodeInterface = { - name: "Test", - path: "/test", - is_dir: false, - content_type: "text/plain", - mod_time: 0, - size: 0, - mode: "", - childs: [], - content: btoa(`mkdir -p \${HOME}/aur/stackdriver-collectd + name: "Test", + path: "/test", + is_dir: false, + content_type: "text/plain", + mod_time: 0, + size: 0, + mode: "", + childs: [], + content: btoa(`mkdir -p \${HOME}/aur/stackdriver-collectd git -C \${HOME}/aur/stackdriver-collectd clone \\ ssh://aur@aur.archlinux.org/stackdriver-collectd.git . @@ -44,17 +44,17 @@ sudo systemctl status stackdriver-collectd sudo systemctl restart telegraf sudo systemctl status telegraf `), -} +}; let opts = { - id: "editor", - is_editable: true, - OnSelection: (begin: number, end: number) => { - console.log("OnSelection: ", begin, end) - }, - OnSave: (content: string) => { - console.log("OnSave: ", content) - } -} -let wuiEditor = new WuiEditor(opts) -wuiEditor.Open(nodeFile) + id: "editor", + is_editable: true, + OnSelection: (begin: number, end: number) => { + console.log("OnSelection: ", begin, end); + }, + OnSave: (content: string) => { + console.log("OnSave: ", content); + }, +}; +let wuiEditor = new WuiEditor(opts); +wuiEditor.Open(nodeFile); diff --git a/editor/index.html b/editor/index.html index 46f5441..04a64f5 100644 --- a/editor/index.html +++ b/editor/index.html @@ -2,12 +2,15 @@ <!-- SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info> --> <!-- SPDX-License-Identifier: GPL-3.0-or-later --> <html> - <head> - <meta charset="UTF-8" /> - <title>WUI - editor</title> - </head> - <body> - <div id="editor"></div> - <script type="module" src="/editor/example.js"></script> - </body> + +<head> + <meta charset="UTF-8" /> + <title>WUI - editor</title> +</head> + +<body> + <div id="editor"></div> + <script type="module" src="/editor/example.js"></script> +</body> + </html> @@ -2,14 +2,17 @@ <!-- SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info --> <!-- SPDX-License-Identifier: GPL-3.0-or-later --> <html> - <head> - <meta charset="UTF-8" /> - <title>WUI - web user interface</title> - </head> - <body> - <h3><a href="/editor"> Editor </a></h3> - <h3><a href="/input"> Input </a></h3> - <h3><a href="/notif"> Notification </a></h3> - <h3><a href="/vfs"> VFS </a></h3> - </body> + +<head> + <meta charset="UTF-8" /> + <title>WUI - web user interface</title> +</head> + +<body> + <h3><a href="/editor"> Editor </a></h3> + <h3><a href="/input"> Input </a></h3> + <h3><a href="/notif"> Notification </a></h3> + <h3><a href="/vfs"> VFS </a></h3> +</body> + </html> diff --git a/input/checkboxes.ts b/input/checkboxes.ts index 480859d..72d3da8 100644 --- a/input/checkboxes.ts +++ b/input/checkboxes.ts @@ -1,32 +1,33 @@ // SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info> // SPDX-License-Identifier: GPL-3.0-or-later -import { WuiInputOption } from "./option.js" +import { WuiInputOption } from "./option.js"; export interface WuiKeyValue { - [key: string]: string + [key: string]: string; } export interface WuiKeySelectOption { - [key: string]: WuiInputOption + [key: string]: WuiInputOption; } export interface WuiInputCheckboxesOpts { - label: string - name: string - options: WuiKeySelectOption - id?: string - hint?: string - is_disabled?: boolean - is_hint_toggled?: boolean - onChangeHandler?: (values: string[]) => void + label: string; + name: string; + options: WuiKeySelectOption; + id?: string; + hint?: string; + is_disabled?: boolean; + is_hint_toggled?: boolean; + onChangeHandler?: (values: string[]) => void; } -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" +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 @@ -57,124 +58,124 @@ const WUI_INPUT_CHECKBOXES_CLASS_LABEL = "wui_input_checkboxes_label" // The onChangeHandler receive all checked values. // export class WuiInputCheckboxes { - el: HTMLElement - private el_label!: HTMLElement - private el_fieldset!: HTMLFieldSetElement - private el_hint!: HTMLElement - private el_hint_toggler!: HTMLElement - private values: string[] = [] + el: HTMLElement; + private el_label!: HTMLElement; + private el_fieldset!: HTMLFieldSetElement; + private el_hint!: HTMLElement; + private el_hint_toggler!: HTMLElement; + private values: string[] = []; - constructor(public opts: WuiInputCheckboxesOpts) { - 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" + constructor(public opts: WuiInputCheckboxesOpts) { + 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) + this.generateLabel(this.el); + if (opts.hint) { + this.generateHintToggler(this.el); + } + this.generateInput(this.el); - if (opts.hint) { - this.generateHint() - } - } + if (opts.hint) { + this.generateHint(); + } + } - private generateLabel(wrapper: HTMLElement) { - this.el_label = document.createElement("label") - this.el_label.classList.add(WUI_INPUT_CHECKBOXES_CLASS_LABEL) - this.el_label.innerHTML = `${this.opts.label} ` - wrapper.appendChild(this.el_label) - } + private generateLabel(wrapper: HTMLElement) { + this.el_label = document.createElement("label"); + this.el_label.classList.add(WUI_INPUT_CHECKBOXES_CLASS_LABEL); + this.el_label.innerHTML = `${this.opts.label} `; + wrapper.appendChild(this.el_label); + } - private generateInput(wrapper: HTMLElement) { - this.el_fieldset = document.createElement("fieldset") - this.el_fieldset.classList.add(WUI_INPUT_CHECKBOXES_CLASS_INPUT) + private generateInput(wrapper: HTMLElement) { + this.el_fieldset = document.createElement("fieldset"); + this.el_fieldset.classList.add(WUI_INPUT_CHECKBOXES_CLASS_INPUT); - for (let key in this.opts.options) { - let option = this.opts.options[key] - let value = option.value + for (let key in this.opts.options) { + let option = this.opts.options[key]; + let value = option.value; - let wrapper = document.createElement("div") + let wrapper = document.createElement("div"); - let el_cb = document.createElement("input") - el_cb.type = "checkbox" - el_cb.name = this.opts.name - el_cb.value = option.value - if (option.selected) { - el_cb.checked = true - this.values.push(value) - } - el_cb.onclick = () => { - this.onClickCheckbox(el_cb.value, el_cb.checked) - } - wrapper.appendChild(el_cb) + let el_cb = document.createElement("input"); + el_cb.type = "checkbox"; + el_cb.name = this.opts.name; + el_cb.value = option.value; + if (option.selected) { + el_cb.checked = true; + this.values.push(value); + } + el_cb.onclick = () => { + this.onClickCheckbox(el_cb.value, el_cb.checked); + }; + wrapper.appendChild(el_cb); - let el_label = document.createElement("label") - el_label.innerHTML = key - wrapper.appendChild(el_label) + let el_label = document.createElement("label"); + el_label.innerHTML = key; + wrapper.appendChild(el_label); - this.el_fieldset.appendChild(wrapper) - } + this.el_fieldset.appendChild(wrapper); + } - if (this.opts.is_disabled) { - this.el_fieldset.disabled = true - } + if (this.opts.is_disabled) { + this.el_fieldset.disabled = true; + } - wrapper.appendChild(this.el_fieldset) - } + wrapper.appendChild(this.el_fieldset); + } - private generateHintToggler(wrapper: HTMLElement) { - this.el_hint_toggler = document.createElement("span") - this.el_hint_toggler.classList.add(WUI_INPUT_CHECKBOXES_CLASS_HINT_TOGGLER) - this.el_hint_toggler.innerHTML = " ℹ" + private generateHintToggler(wrapper: HTMLElement) { + 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) - } + 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_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) - } + private 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); + } - private onClickHintToggler() { - if (this.el_hint.style.display === "none") { - this.el_hint.style.display = "block" - } else { - this.el_hint.style.display = "none" - } - } + private onClickHintToggler() { + if (this.el_hint.style.display === "none") { + this.el_hint.style.display = "block"; + } else { + this.el_hint.style.display = "none"; + } + } - private onClickCheckbox(value: string, selected: boolean) { - 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) - } - } + private onClickCheckbox(value: string, selected: boolean) { + 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.ts b/input/example.ts index 05555f1..660ea20 100644 --- a/input/example.ts +++ b/input/example.ts @@ -1,242 +1,242 @@ // SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info> // SPDX-License-Identifier: GPL-3.0-or-later -import { WuiInputString, WuiInputStringOpts } from "./string.js" -import { WuiInputNumber, WuiInputNumberOpts } from "./number.js" -import { WuiInputSelect, WuiInputSelectOpts } from "./select.js" -import { WuiInputCheckboxes, WuiInputCheckboxesOpts } from "./checkboxes.js" +import { WuiInputString, WuiInputStringOpts } from "./string.js"; +import { WuiInputNumber, WuiInputNumberOpts } from "./number.js"; +import { WuiInputSelect, WuiInputSelectOpts } from "./select.js"; +import { WuiInputCheckboxes, WuiInputCheckboxesOpts } from "./checkboxes.js"; function exampleInputString() { - let el_example = document.createElement("div") + let el_example = document.createElement("div"); - let el_title = document.createElement("h3") - el_title.innerText = "Input string" - el_example.appendChild(el_title) + let el_title = document.createElement("h3"); + el_title.innerText = "Input string"; + el_example.appendChild(el_title); - let el_out = document.createElement("span") + let el_out = document.createElement("span"); - let opts: WuiInputStringOpts = { - id: "my_input_string", - label: "Input string with ID", - value: "Hello, input string", - hint: "The input ID is 'my_input_string'", - onChangeHandler: (v: string) => { - el_out.innerText = v - }, - } - let el_input_string = new WuiInputString(opts) - el_example.appendChild(el_input_string.el) + let opts: WuiInputStringOpts = { + id: "my_input_string", + label: "Input string with ID", + value: "Hello, input string", + hint: "The input ID is 'my_input_string'", + onChangeHandler: (v: string) => { + el_out.innerText = v; + }, + }; + let el_input_string = new WuiInputString(opts); + el_example.appendChild(el_input_string.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: string) => { - el_out.innerText = v - }, - } - el_input_string = new WuiInputString(opts) - el_example.appendChild(el_input_string.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: string) => { + el_out.innerText = v; + }, + }; + el_input_string = new WuiInputString(opts); + el_example.appendChild(el_input_string.el); - opts = { - label: "Input string without hint", - value: "Hello, input string without hint", - onChangeHandler: (v: string) => { - el_out.innerText = v - }, - } - el_input_string = new WuiInputString(opts) - el_example.appendChild(el_input_string.el) + opts = { + label: "Input string without hint", + value: "Hello, input string without hint", + onChangeHandler: (v: string) => { + el_out.innerText = v; + }, + }; + el_input_string = new WuiInputString(opts); + el_example.appendChild(el_input_string.el); - let el_out_label = document.createElement("div") - el_out_label.innerText = "Input string changes to " - el_out_label.appendChild(el_out) - el_example.appendChild(el_out_label) + let el_out_label = document.createElement("div"); + el_out_label.innerText = "Input string changes to "; + el_out_label.appendChild(el_out); + el_example.appendChild(el_out_label); - document.body.appendChild(el_example) + document.body.appendChild(el_example); } function exampleInputNumber() { - let el_example = document.createElement("div") + let el_example = document.createElement("div"); - let el_title = document.createElement("h3") - el_title.innerText = "Input number" - el_example.appendChild(el_title) + let el_title = document.createElement("h3"); + el_title.innerText = "Input number"; + el_example.appendChild(el_title); - let el_out = document.createElement("span") + let el_out = document.createElement("span"); - let opts: WuiInputNumberOpts = { - label: "Input number", - value: 1, - onChangeHandler: (val: number) => { - el_out.innerText = ""+val - }, - } - let input_num = new WuiInputNumber(opts) - el_example.appendChild(input_num.el) + let opts: WuiInputNumberOpts = { + label: "Input number", + value: 1, + onChangeHandler: (val: number) => { + el_out.innerText = "" + val; + }, + }; + let input_num = new WuiInputNumber(opts); + el_example.appendChild(input_num.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: number) => { - el_out.innerText = ""+val - }, - } - input_num = new WuiInputNumber(opts) - el_example.appendChild(input_num.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: number) => { + el_out.innerText = "" + val; + }, + }; + input_num = new WuiInputNumber(opts); + el_example.appendChild(input_num.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: number) => { - el_out.innerText = ""+val - }, - } - input_num = new WuiInputNumber(opts) - el_example.appendChild(input_num.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: number) => { + el_out.innerText = "" + val; + }, + }; + input_num = new WuiInputNumber(opts); + el_example.appendChild(input_num.el); - opts = { - label: "Input number with hint", - value: 10000, - hint: "This is the <b>hint</b>", - onChangeHandler: (val: number) => { - el_out.innerText = ""+val - }, - } - input_num = new WuiInputNumber(opts) - el_example.appendChild(input_num.el) + opts = { + label: "Input number with hint", + value: 10000, + hint: "This is the <b>hint</b>", + onChangeHandler: (val: number) => { + el_out.innerText = "" + 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) => { - el_out.innerText = ""+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) => { + el_out.innerText = "" + val; + }, + }; + input_num = new WuiInputNumber(opts); + el_example.appendChild(input_num.el); - let el_out_label = document.createElement("div") - el_out_label.innerText = "Input number changes to " - el_out_label.appendChild(el_out) - el_example.appendChild(el_out_label) + let el_out_label = document.createElement("div"); + el_out_label.innerText = "Input number changes to "; + el_out_label.appendChild(el_out); + el_example.appendChild(el_out_label); - document.body.appendChild(el_example) + document.body.appendChild(el_example); } function exampleInputSelect() { - let el_example = document.createElement("div") - document.body.appendChild(el_example) + 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_title = document.createElement("h3"); + el_title.innerText = "Input select"; + el_example.appendChild(el_title); - let el_log = document.createElement("div") + 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 one of the option to see the price.", - is_hint_toggled: true, - 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) + 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 one of the option to see the price.", + is_hint_toggled: true, + 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); } function exampleInputCheckboxes() { - let el_example = document.createElement("div") - document.body.appendChild(el_example) + let el_example = document.createElement("div"); + document.body.appendChild(el_example); - let el_title = document.createElement("h3") - el_title.innerText = "Input checkboxes" - el_example.appendChild(el_title) + let el_title = document.createElement("h3"); + el_title.innerText = "Input checkboxes"; + el_example.appendChild(el_title); - let el_log = document.createElement("div") + let el_log = document.createElement("div"); - let opts: WuiInputCheckboxesOpts = { - 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: string[]) => { - el_log.innerText = `You are selecting ${values}` - }, - } - let input = new WuiInputCheckboxes(opts) - el_example.appendChild(input.el) + let opts: WuiInputCheckboxesOpts = { + 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: string[]) => { + el_log.innerText = `You are selecting ${values}`; + }, + }; + let input = new WuiInputCheckboxes(opts); + el_example.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: string[]) => { - el_log.innerText = `You are selecting ${values}` - }, - } - input = new WuiInputCheckboxes(opts) - el_example.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: string[]) => { + el_log.innerText = `You are selecting ${values}`; + }, + }; + input = new WuiInputCheckboxes(opts); + el_example.appendChild(input.el); - el_example.appendChild(el_log) + el_example.appendChild(el_log); } -exampleInputString() -exampleInputNumber() -exampleInputSelect() -exampleInputCheckboxes() +exampleInputString(); +exampleInputNumber(); +exampleInputSelect(); +exampleInputCheckboxes(); diff --git a/input/index.html b/input/index.html index 2144d51..0e20989 100644 --- a/input/index.html +++ b/input/index.html @@ -2,13 +2,16 @@ <!-- SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info> --> <!-- SPDX-License-Identifier: GPL-3.0-or-later --> <html> - <head> - <meta charset="UTF-8" /> - <title>WUI - input</title> - <style> - </style> - </head> - <body> - <script type="module" src="/input/example.js"></script> - </body> + +<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.ts b/input/number.ts index 309fffd..0b59dbe 100644 --- a/input/number.ts +++ b/input/number.ts @@ -2,24 +2,24 @@ // SPDX-License-Identifier: GPL-3.0-or-later export interface WuiInputNumberOpts { - label: string | HTMLElement - value: number - id?: string - hint?: string - max?: number - min?: number - class_label?: string // Additional CSS class for label. - class_input?: string // Additional CSS class for input. - is_disabled?: boolean - is_hint_toggled?: boolean // If true, the hint will be displayed first instead of hidden. - onChangeHandler: (new_value: number) => void + label: string | HTMLElement; + value: number; + id?: string; + hint?: string; + max?: number; + min?: number; + class_label?: string; // Additional CSS class for label. + class_input?: string; // Additional CSS class for input. + is_disabled?: boolean; + is_hint_toggled?: boolean; // If true, the hint will be displayed first instead of hidden. + 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" +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 @@ -50,162 +50,158 @@ const WUI_INPUT_NUMBER_CLASS_LABEL = "wui_input_number_label" // 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 + 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 + 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" + 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) + 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() - } - } + 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) - if (this.opts.class_label) { - this.el_label.classList.add(this.opts.class_label) - } + private generateLabel(wrapper: HTMLElement) { + 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) - } + 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); + } - private generateInput(wrapper: HTMLElement) { - this.el_input = document.createElement( - "input", - ) as HTMLInputElement - this.el_input.classList.add(WUI_INPUT_NUMBER_CLASS_INPUT) - if (this.opts.class_input) { - this.el_input.classList.add(this.opts.class_input) - } + private generateInput(wrapper: HTMLElement) { + this.el_input = document.createElement("input") as HTMLInputElement; + 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 + 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 - } + 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 (hint !== "") { + this.el_input.title = hint; + } - if (this.opts.is_disabled) { - this.el_input.disabled = true - } + if (this.opts.is_disabled) { + this.el_input.disabled = true; + } - this.el_input.onkeyup = (ev: KeyboardEvent) => { - return this.onKeyUp(ev) - } + this.el_input.onkeyup = (ev: KeyboardEvent) => { + return this.onKeyUp(ev); + }; - wrapper.appendChild(this.el_input) - } + 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 = " ℹ" + 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 = " ℹ"; - 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) - } + 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 || "" + 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 - 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) - } + 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); + } - private onClickHintToggler() { - if (this.el_hint.style.display === "none") { - this.el_hint.style.display = "block" - } else { - this.el_hint.style.display = "none" - } - } + 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 + private onKeyUp(ev: KeyboardEvent) { + let target = ev.target as HTMLInputElement; - ev.preventDefault() - let 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 - } + ev.preventDefault(); + let 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: number) { - this.el_input.value = "" + v - } + // Set the input value. + Set(v: number) { + this.el_input.value = "" + v; + } } diff --git a/input/option.ts b/input/option.ts index f30b84c..bdb21a2 100644 --- a/input/option.ts +++ b/input/option.ts @@ -2,6 +2,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later export interface WuiInputOption { - value: string - selected: boolean + value: string; + selected: boolean; } diff --git a/input/select.ts b/input/select.ts index 8883caf..6bf4810 100644 --- a/input/select.ts +++ b/input/select.ts @@ -1,36 +1,35 @@ // SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info> // SPDX-License-Identifier: GPL-3.0-or-later -import { WuiInputOption } from "./option.js" +import { WuiInputOption } from "./option.js"; export interface WuiKeyValue { - [key: string]: string + [key: string]: string; } export interface WuiKeySelectOption { - [key: string]: WuiInputOption + [key: string]: WuiInputOption; } export interface WuiInputSelectOpts { - label: string | HTMLElement - name: string - options: WuiKeySelectOption - id?: string - hint?: string - class_label?: string // Additional CSS class for label. - class_input?: string // Additional CSS class for input. - is_disabled?: boolean - is_hint_toggled?: boolean - onChangeHandler?: (key: string, value: string) => void + label: string | HTMLElement; + name: string; + options: WuiKeySelectOption; + id?: string; + hint?: string; + class_label?: string; // Additional CSS class for label. + class_input?: string; // Additional CSS class for input. + is_disabled?: boolean; + is_hint_toggled?: 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" +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, @@ -57,145 +56,142 @@ const WUI_INPUT_SELECT_CLASS_LABEL = "wui_input_select_label" // // 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 = "" + 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" + 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) + 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() - } - } + 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) - if (this.opts.class_label) { - this.el_label.classList.add(this.opts.class_label) - } + private generateLabel(wrapper: HTMLElement) { + 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) - } + 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); + } - 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) - if (this.opts.class_input) { - this.el_input.classList.add(this.opts.class_input) - } + 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); + if (this.opts.class_input) { + this.el_input.classList.add(this.opts.class_input); + } - for (let key in this.opts.options) { - let option = this.opts.options[key] + 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 + let el_option = document.createElement("option"); + el_option.value = option.value; + el_option.innerHTML = key; - if (option.selected) { - el_option.selected = true - } + if (option.selected) { + el_option.selected = true; + } - this.el_input.appendChild(el_option) + this.el_input.appendChild(el_option); - this.value_key[option.value] = key - } + this.value_key[option.value] = key; + } - if (this.opts.is_disabled) { - this.el_input.disabled = true - } + 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() - } - } + if (this.opts.onChangeHandler) { + this.el_input.onclick = (ev: Event) => { + ev.preventDefault(); + ev.stopPropagation(); + this.onClickInput(); + }; + } - wrapper.appendChild(this.el_input) - } + 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 = " ℹ" + 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) - } + 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 || "" - 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) - } + 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 || ""; + 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); + } - private onClickHintToggler() { - if (this.el_hint.style.display === "none") { - this.el_hint.style.display = "block" - } else { - this.el_hint.style.display = "none" - } - } + 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 - } + 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 - } - } + let value = this.el_input.value; + let key = this.value_key[value]; + if (this.value !== value) { + this.opts.onChangeHandler(key, value); + this.value = value; + } + } - // Set the input value. - Set(v: string) { - this.el_input.value = v - } + // Set the input value. + Set(v: string) { + this.el_input.value = v; + } } diff --git a/input/string.ts b/input/string.ts index 2381a01..d428c32 100644 --- a/input/string.ts +++ b/input/string.ts @@ -2,24 +2,23 @@ // SPDX-License-Identifier: GPL-3.0-or-later export interface WuiInputStringOpts { - label: string | HTMLElement - value: string - id?: string - hint?: string - class_label?: string // Additional CSS class for label. - class_input?: string // Additional CSS class for input. - is_disabled?: boolean - is_hint_toggled?: boolean // If true, the hint will be displayed first instead of hidden. - onChangeHandler?: (new_value: string) => void + label: string | HTMLElement; + value: string; + id?: string; + hint?: string; + class_label?: string; // Additional CSS class for label. + class_input?: string; // Additional CSS class for input. + is_disabled?: boolean; + is_hint_toggled?: boolean; // If true, the hint will be displayed first instead of hidden. + onChangeHandler?: (new_value: string) => void; } -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" +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". // @@ -45,129 +44,118 @@ const WUI_INPUT_STRING_CLASS_LABEL = "wui_input_string_label" // // User can set onChangeHandler to receive new value when the input value // changes. -// export class WuiInputString { - el: HTMLElement - private el_label!: HTMLElement - private el_input!: HTMLInputElement - private el_hint!: HTMLElement - private el_hint_toggler!: HTMLElement - private value: string = "" + el: HTMLElement; + private el_label!: HTMLElement; + private el_input!: HTMLInputElement; + private el_hint!: HTMLElement; + private el_hint_toggler!: HTMLElement; + private value: string = ""; - constructor(public opts: WuiInputStringOpts) { - this.value = opts.value + constructor(public opts: WuiInputStringOpts) { + 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" + 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"; - let wrapper = document.createElement("div") - this.generateLabel(wrapper) - this.generateInput(wrapper) - if (opts.hint) { - this.generateHintToggler(wrapper) - } - this.el.appendChild(wrapper) + 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() - } - } + if (opts.hint) { + this.generateHint(); + } + } - private generateLabel(wrapper: HTMLElement) { - 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) - } + private generateLabel(wrapper: HTMLElement) { + 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) - } + 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); + } - private generateInput(wrapper: HTMLElement) { - this.el_input = document.createElement( - "input", - ) as HTMLInputElement - this.el_input.classList.add(WUI_INPUT_STRING_CLASS_INPUT) - if (this.opts.class_input) { - this.el_input.classList.add(this.opts.class_input) - } + private generateInput(wrapper: HTMLElement) { + this.el_input = document.createElement("input") as HTMLInputElement; + 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 + this.el_input.value = "" + this.opts.value; - if (this.opts.is_disabled) { - this.el_input.disabled = true - } + if (this.opts.is_disabled) { + this.el_input.disabled = true; + } - if (this.opts.onChangeHandler) { - this.el_input.onkeyup = (ev: Event) => { - if (this.opts.onChangeHandler) { - if ( - this.value !== - this.el_input.value - ) { - this.opts.onChangeHandler( - this.el_input.value, - ) - this.value = - this.el_input.value - } - } - } - } + if (this.opts.onChangeHandler) { + this.el_input.onkeyup = (ev: Event) => { + 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) - } + wrapper.appendChild(this.el_input); + } - private generateHintToggler(wrapper: HTMLElement) { - this.el_hint_toggler = document.createElement("span") - this.el_hint_toggler.classList.add( - WUI_INPUT_STRING_CLASS_HINT_TOGGLER, - ) - this.el_hint_toggler.innerHTML = " ℹ" + private generateHintToggler(wrapper: HTMLElement) { + 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) - } + 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_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) - } + private 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); + } - private onClickHintToggler() { - if (this.el_hint.style.display === "none") { - this.el_hint.style.display = "block" - } else { - this.el_hint.style.display = "none" - } - } + private 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: string) { - this.el_input.value = v - } + // Set the input value. + Set(v: string) { + this.el_input.value = v; + } } diff --git a/notif/example.ts b/notif/example.ts index e803145..a15f986 100644 --- a/notif/example.ts +++ b/notif/example.ts @@ -1,55 +1,59 @@ // SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info> // SPDX-License-Identifier: GPL-3.0-or-later -import { WuiNotif, WUI_NOTIF_CLASS_ERROR, WUI_NOTIF_CLASS_INFO } from "./notif.js" +import { + WuiNotif, + WUI_NOTIF_CLASS_ERROR, + WUI_NOTIF_CLASS_INFO, +} from "./notif.js"; -let inputMsg: HTMLTextAreaElement -let wuiNotif: WuiNotif +let inputMsg: HTMLTextAreaElement; +let wuiNotif: WuiNotif; function main() { - wuiNotif = new WuiNotif() + wuiNotif = new WuiNotif(); - inputMsg = document.createElement("textarea") as HTMLTextAreaElement - inputMsg.id = "input_msg" - inputMsg.value = `Hello world, this is a notification with HTML format using <b>bold</b> and <u>underline</u> words.` - document.body.appendChild(inputMsg) + inputMsg = document.createElement("textarea") as HTMLTextAreaElement; + inputMsg.id = "input_msg"; + inputMsg.value = `Hello world, this is a notification with HTML format using <b>bold</b> and <u>underline</u> words.`; + document.body.appendChild(inputMsg); - let el_wrapper = document.createElement("div") - el_wrapper.style.marginTop = "10px" - document.body.appendChild(el_wrapper) + let el_wrapper = document.createElement("div"); + el_wrapper.style.marginTop = "10px"; + document.body.appendChild(el_wrapper); - let el_button_info = document.createElement("button") - el_button_info.innerText = "Info" - el_button_info.style.marginRight = "10px" - el_button_info.onclick = notifInfo - el_wrapper.appendChild(el_button_info) + let el_button_info = document.createElement("button"); + el_button_info.innerText = "Info"; + el_button_info.style.marginRight = "10px"; + el_button_info.onclick = notifInfo; + el_wrapper.appendChild(el_button_info); - let el_button_error = document.createElement("button") - el_button_error.innerText = "Error" - el_button_error.onclick = notifError - el_wrapper.appendChild(el_button_error) + let el_button_error = document.createElement("button"); + el_button_error.innerText = "Error"; + el_button_error.onclick = notifError; + el_wrapper.appendChild(el_button_error); - document.body.appendChild(document.createElement("p")) + document.body.appendChild(document.createElement("p")); - let previewError = document.createElement("div") - previewError.classList.add(`${WUI_NOTIF_CLASS_ERROR}`) - previewError.innerText = `Preview of error style` - document.body.appendChild(previewError) + let previewError = document.createElement("div"); + previewError.classList.add(`${WUI_NOTIF_CLASS_ERROR}`); + previewError.innerText = `Preview of error style`; + document.body.appendChild(previewError); - let previewInfo = document.createElement("div") - previewInfo.classList.add(`${WUI_NOTIF_CLASS_INFO}`) - previewInfo.innerText = `Preview of info style` - document.body.appendChild(previewInfo) + let previewInfo = document.createElement("div"); + previewInfo.classList.add(`${WUI_NOTIF_CLASS_INFO}`); + previewInfo.innerText = `Preview of info style`; + document.body.appendChild(previewInfo); } function notifInfo() { - wuiNotif.Info(inputMsg.value) + wuiNotif.Info(inputMsg.value); } function notifError() { - wuiNotif.Error(inputMsg.value) + wuiNotif.Error(inputMsg.value); } //---- -main() +main(); diff --git a/notif/index.html b/notif/index.html index 7797b29..5f00107 100644 --- a/notif/index.html +++ b/notif/index.html @@ -2,21 +2,25 @@ <!-- SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info --> <!-- SPDX-License-Identifier: GPL-3.0-or-later --> <html> - <head> - <meta charset="UTF-8" /> - <title>WUI - notif</title> - <style> - #input_msg { - width: calc(100% - 1em); - height: 4em; - } - .empty { - height: 100vh; - } - </style> - </head> - <body> - <p class="empty">Empty spaces.</p> - <script type="module" src="/notif/example.js"></script> - </body> + +<head> + <meta charset="UTF-8" /> + <title>WUI - notif</title> + <style> + #input_msg { + width: calc(100% - 1em); + height: 4em; + } + + .empty { + height: 100vh; + } + </style> +</head> + +<body> + <p class="empty">Empty spaces.</p> + <script type="module" src="/notif/example.js"></script> +</body> + </html> diff --git a/notif/notif.ts b/notif/notif.ts index 63f91cc..8bc5d84 100644 --- a/notif/notif.ts +++ b/notif/notif.ts @@ -1,57 +1,55 @@ // SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info> // SPDX-License-Identifier: GPL-3.0-or-later -export const WUI_NOTIF_ID = "wui_notif" -export const WUI_NOTIF_CLASS_INFO = "wui_notif_info" -export const WUI_NOTIF_CLASS_ERROR = "wui_notif_error" +export const WUI_NOTIF_ID = "wui_notif"; +export const WUI_NOTIF_CLASS_INFO = "wui_notif_info"; +export const WUI_NOTIF_CLASS_ERROR = "wui_notif_error"; -// // WuiNotif implement the HTML interface to display pop-up notification. // The notification can be triggered by calling method Info() or Error(). // Each pop-up has 5 seconds duration, after that they will be removed // automatically. -// export class WuiNotif { - private el: HTMLElement - private timeout: number = 5000 // 5 seconds timeout + private el: HTMLElement; + private timeout: number = 5000; // 5 seconds timeout - constructor() { - this.el = document.createElement("div") - this.el.id = WUI_NOTIF_ID + constructor() { + this.el = document.createElement("div"); + this.el.id = WUI_NOTIF_ID; - document.body.appendChild(this.el) + document.body.appendChild(this.el); - this.initStyle() - } + this.initStyle(); + } - // Info show the msg as information. - Info(msg: string) { - let item = document.createElement("div") - item.innerHTML = msg - item.classList.add(WUI_NOTIF_CLASS_INFO) - this.el.appendChild(item) + // Info show the msg as information. + Info(msg: string) { + let item = document.createElement("div"); + item.innerHTML = msg; + item.classList.add(WUI_NOTIF_CLASS_INFO); + this.el.appendChild(item); - setTimeout(() => { - this.el.removeChild(item) - }, this.timeout) - } + setTimeout(() => { + this.el.removeChild(item); + }, this.timeout); + } - // Info show the msg as an error. - Error(msg: string) { - let item = document.createElement("div") - item.innerHTML = msg - item.classList.add(WUI_NOTIF_CLASS_ERROR) - this.el.appendChild(item) + // Info show the msg as an error. + Error(msg: string) { + let item = document.createElement("div"); + item.innerHTML = msg; + item.classList.add(WUI_NOTIF_CLASS_ERROR); + this.el.appendChild(item); - setTimeout(() => { - this.el.removeChild(item) - }, this.timeout) - } + setTimeout(() => { + this.el.removeChild(item); + }, this.timeout); + } - private initStyle() { - let style = document.createElement("style") - style.type = "text/css" - style.innerText = ` + private initStyle() { + let style = document.createElement("style"); + style.type = "text/css"; + style.innerText = ` #${WUI_NOTIF_ID} { left: 10%; position: fixed; @@ -71,7 +69,7 @@ export class WuiNotif { margin-bottom: 1em; padding: 1em; } - ` - document.head.appendChild(style) - } + `; + document.head.appendChild(style); + } } diff --git a/response.ts b/response.ts index 6415636..f74161a 100644 --- a/response.ts +++ b/response.ts @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info> // SPDX-License-Identifier: GPL-3.0-or-later -// // WuiResponseInterface define an interface that will be returned by function // or method with non-void type. // @@ -10,9 +9,8 @@ // // If the function/method call failed, the code should be set to other value // beside 200 with a message describe why its failed. -// export interface WuiResponseInterface { - code: number - message: string - data?: any + code: number; + message: string; + data?: any; } diff --git a/tsconfig.json b/tsconfig.json index c9ed64f..bcb70d1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,12 +1,12 @@ { - "compilerOptions": { - "declaration": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "inlineSourceMap": true, - "lib": ["es2015", "dom", "es2015.promise"], - "module": "es2015", - "strict": true, - "target": "es2015" - } + "compilerOptions": { + "declaration": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "inlineSourceMap": true, + "lib": ["es2015", "dom", "es2015.promise"], + "module": "es2015", + "strict": true, + "target": "es2015" + } } diff --git a/vfs/example.ts b/vfs/example.ts index 6a1c833..f2bc91d 100644 --- a/vfs/example.ts +++ b/vfs/example.ts @@ -1,162 +1,167 @@ // SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info> // SPDX-License-Identifier: GPL-3.0-or-later -import { WuiVfs, WuiVfsNodeInterface } from "./vfs.js" -import { WuiResponseInterface } from "../response.js" +import { WuiVfs, WuiVfsNodeInterface } from "./vfs.js"; +import { WuiResponseInterface } from "../response.js"; interface PathNodeInterface { - [key: string]: WuiVfsNodeInterface + [key: string]: WuiVfsNodeInterface; } let dummyfs: PathNodeInterface = { - "/": { - name: "/", - path: "/", - is_dir: true, - content: "", - childs: [ - { - name: "Dir 1", - path: "/Dir 1", - is_dir: true, - content: "", - childs: [ - { - name: "File 1.1", - path: "/Dir 1/File 1.1", - is_dir: false, - content: "This is the content of File 1.1", - }, - { - name: `File 1.2`, - path: "/Dir 1/File 1.2", - is_dir: false, - content: "This is the content of File 1.2", - }, - ], - }, - { - name: "Dir 2", - path: "/Dir 2", - is_dir: true, - content: "", - childs: [ - { - name: "File 2.1", - path: "/Dir 2/File 2.1", - is_dir: false, - content: "This is the content of File 2.1", - }, - { - name: "File 2.2", - path: "/Dir 2/File 2.2", - is_dir: false, - content: "This is the content of File 2.2", - }, - ], - }, - ], - }, - "/Dir 1": { - name: "Dir 1", - path: "/Dir 1", - is_dir: true, - content: "", - childs: [ - { - name: "File 1.1", - path: "/Dir 1/File 1.1", - is_dir: false, - content: "This is the content of File 1.1", - }, - { - name: "File 1.2", - path: "/Dir 1/File 1.2", - is_dir: false, - content: "This is the content of File 1.2", - }, - ], - }, - "/Dir 2": { - name: "Dir 2", - path: "/Dir 2", - is_dir: true, - content: "", - childs: [ - { - name: "File 2.1", - path: "/Dir 2/File 2.1", - is_dir: false, - content: "This is the content of File 2.1", - }, - { - name: "File 2.2", - path: "/Dir 2/File 2.2", - is_dir: false, - content: "This is the content of File 2.2", - }, - ], - } -} + "/": { + name: "/", + path: "/", + is_dir: true, + content: "", + childs: [ + { + name: "Dir 1", + path: "/Dir 1", + is_dir: true, + content: "", + childs: [ + { + name: "File 1.1", + path: "/Dir 1/File 1.1", + is_dir: false, + content: "This is the content of File 1.1", + }, + { + name: `File 1.2`, + path: "/Dir 1/File 1.2", + is_dir: false, + content: "This is the content of File 1.2", + }, + ], + }, + { + name: "Dir 2", + path: "/Dir 2", + is_dir: true, + content: "", + childs: [ + { + name: "File 2.1", + path: "/Dir 2/File 2.1", + is_dir: false, + content: "This is the content of File 2.1", + }, + { + name: "File 2.2", + path: "/Dir 2/File 2.2", + is_dir: false, + content: "This is the content of File 2.2", + }, + ], + }, + ], + }, + "/Dir 1": { + name: "Dir 1", + path: "/Dir 1", + is_dir: true, + content: "", + childs: [ + { + name: "File 1.1", + path: "/Dir 1/File 1.1", + is_dir: false, + content: "This is the content of File 1.1", + }, + { + name: "File 1.2", + path: "/Dir 1/File 1.2", + is_dir: false, + content: "This is the content of File 1.2", + }, + ], + }, + "/Dir 2": { + name: "Dir 2", + path: "/Dir 2", + is_dir: true, + content: "", + childs: [ + { + name: "File 2.1", + path: "/Dir 2/File 2.1", + is_dir: false, + content: "This is the content of File 2.1", + }, + { + name: "File 2.2", + path: "/Dir 2/File 2.2", + is_dir: false, + content: "This is the content of File 2.2", + }, + ], + }, +}; async function main() { - let opts = { - id: "vfs", - Open: Open, - OpenNode: OpenNode, - } + let opts = { + id: "vfs", + Open: Open, + OpenNode: OpenNode, + }; - let wui_vfs = new WuiVfs(opts) - wui_vfs.OpenDir("/") + let wui_vfs = new WuiVfs(opts); + wui_vfs.OpenDir("/"); } -async function Open(path: string, is_dir: boolean): Promise<WuiResponseInterface> { - console.log("Open:", path, is_dir) - let res: WuiResponseInterface = { - code: 200, - message: "", - } +async function Open( + path: string, + is_dir: boolean, +): Promise<WuiResponseInterface> { + console.log("Open:", path, is_dir); + let res: WuiResponseInterface = { + code: 200, + message: "", + }; - if (is_dir) { - res.data = dummyfs[path] - return res - } + if (is_dir) { + res.data = dummyfs[path]; + return res; + } - res.data = { - name: "", - path: path, - content: "", - } + res.data = { + name: "", + path: path, + content: "", + }; - switch (path) { - case "/Dir 1/File 1.1": - res.data.name = "File 1.1" - res.data.content = "This is the content of " + res.data.name - break - case "/Dir 1/File 1.2": - res.data.name = "File 1.2" - res.data.content = "This is the content of " + res.data.name - break - case "/Dir 2/File 2.1": - res.data.name = "File 2.1" - res.data.content = "This is the content of " + res.data.name - break - case "/Dir 2/File 2.2": - res.data.name = "File 2.1" - res.data.content = "This is the content of " + res.data.name - break - default: - res.code = 404 - res.message = "path not found" - } + switch (path) { + case "/Dir 1/File 1.1": + res.data.name = "File 1.1"; + res.data.content = "This is the content of " + res.data.name; + break; + case "/Dir 1/File 1.2": + res.data.name = "File 1.2"; + res.data.content = "This is the content of " + res.data.name; + break; + case "/Dir 2/File 2.1": + res.data.name = "File 2.1"; + res.data.content = "This is the content of " + res.data.name; + break; + case "/Dir 2/File 2.2": + res.data.name = "File 2.1"; + res.data.content = "This is the content of " + res.data.name; + break; + default: + res.code = 404; + res.message = "path not found"; + } - console.log("Open:", res) + console.log("Open:", res); - return res + return res; } -async function OpenNode(node: WuiVfsNodeInterface): Promise<WuiResponseInterface> { - return await Open(node.path, node.is_dir) +async function OpenNode( + node: WuiVfsNodeInterface, +): Promise<WuiResponseInterface> { + return await Open(node.path, node.is_dir); } -main() +main(); diff --git a/vfs/index.html b/vfs/index.html index 9ec2bd5..eed4206 100644 --- a/vfs/index.html +++ b/vfs/index.html @@ -2,12 +2,15 @@ <!-- SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info --> <!-- SPDX-License-Identifier: GPL-3.0-or-later --> <html> - <head> - <meta charset="UTF-8" /> - <title>WUI - Virtual File System (vfs)</title> - </head> - <body> - <div id="vfs"></div> - <script type="module" src="/vfs/example.js"></script> - </body> + +<head> + <meta charset="UTF-8" /> + <title>WUI - Virtual File System (vfs)</title> +</head> + +<body> + <div id="vfs"></div> + <script type="module" src="/vfs/example.js"></script> +</body> + </html> @@ -1,195 +1,195 @@ // SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info> // SPDX-License-Identifier: GPL-3.0-or-later -import { WuiResponseInterface } from "../response" +import { WuiResponseInterface } from "../response"; -const CLASS_VFS_PATH = "wui_vfs_path" -const CLASS_VFS_LIST = "wui_vfs_list" +const CLASS_VFS_PATH = "wui_vfs_path"; +const CLASS_VFS_LIST = "wui_vfs_list"; export interface WuiVfsNodeInterface { - name: string - path: string - is_dir: boolean - content_type?: string - mod_time?: number - size?: number - mode?: string - childs?: WuiVfsNodeInterface[] - content: string // If its not empty, it MUST be encoded in base64. + name: string; + path: string; + is_dir: boolean; + content_type?: string; + mod_time?: number; + size?: number; + mode?: string; + childs?: WuiVfsNodeInterface[]; + content: string; // If its not empty, it MUST be encoded in base64. } -type PathClickHandler = (path: string) => void -type NodeClickHandler = (node: WuiVfsNodeInterface) => void +type PathClickHandler = (path: string) => void; +type NodeClickHandler = (node: WuiVfsNodeInterface) => void; export interface WuiVfsOptions { - id: string + id: string; - // Open define an handler that will be called when a directory is clicked - // from the WuiVfsPath. - Open(path: string, is_dir: boolean): Promise<WuiResponseInterface> + // Open define an handler that will be called when a directory is clicked + // from the WuiVfsPath. + Open(path: string, is_dir: boolean): Promise<WuiResponseInterface>; - // OpenNode define an handler that will be called when a file is clicked - // from the WuiVfsList. - OpenNode(node: WuiVfsNodeInterface): Promise<WuiResponseInterface> + // OpenNode define an handler that will be called when a file is clicked + // from the WuiVfsList. + OpenNode(node: WuiVfsNodeInterface): Promise<WuiResponseInterface>; } export class WuiVfs { - private el!: HTMLElement - private com_path!: WuiVfsPath - private com_list!: WuiVfsList + private el!: HTMLElement; + private com_path!: WuiVfsPath; + private com_list!: WuiVfsList; - constructor(public opts: WuiVfsOptions) { - this.opts = opts + constructor(public opts: WuiVfsOptions) { + this.opts = opts; - let el = document.getElementById(opts.id) - if (!el) { - console.error("WuiVfs: element id", opts.id, "not found") - return - } - this.el = el + let el = document.getElementById(opts.id); + if (!el) { + console.error("WuiVfs: element id", opts.id, "not found"); + return; + } + this.el = el; - this.com_path = new WuiVfsPath((path: string) => { - this.OpenDir(path) - }) - this.el.appendChild(this.com_path.el) + this.com_path = new WuiVfsPath((path: string) => { + this.OpenDir(path); + }); + this.el.appendChild(this.com_path.el); - this.com_list = new WuiVfsList((node: WuiVfsNodeInterface) => { - this.OpenNode(node) - }) - this.el.appendChild(this.com_list.el) - } + this.com_list = new WuiVfsList((node: WuiVfsNodeInterface) => { + this.OpenNode(node); + }); + this.el.appendChild(this.com_list.el); + } - // OpenNode is a handler that will be called when a node is clicked - // inside the WuiVfsList. - OpenNode(node: WuiVfsNodeInterface): void { - if (node.is_dir) { - this.OpenDir(node.path) - } else { - this.opts.OpenNode(node) - } - } + // OpenNode is a handler that will be called when a node is clicked + // inside the WuiVfsList. + OpenNode(node: WuiVfsNodeInterface): void { + if (node.is_dir) { + this.OpenDir(node.path); + } else { + this.opts.OpenNode(node); + } + } - // OpenDir is a handler that will be called when a path is clicked - // inside the WuiVfsPath. - async OpenDir(path: string): Promise<void> { - let res = await this.opts.Open(path, true) - if (res.code != 200) { - return - } - this.Set(res.data as WuiVfsNodeInterface) - } + // OpenDir is a handler that will be called when a path is clicked + // inside the WuiVfsPath. + async OpenDir(path: string): Promise<void> { + let res = await this.opts.Open(path, true); + if (res.code != 200) { + return; + } + this.Set(res.data as WuiVfsNodeInterface); + } - Set(node: WuiVfsNodeInterface) { - if (node.is_dir) { - this.com_path.Open(node) - this.com_list.Open(node) - } - } + Set(node: WuiVfsNodeInterface) { + if (node.is_dir) { + this.com_path.Open(node); + this.com_list.Open(node); + } + } } class WuiVfsList { - el: HTMLElement - node: WuiVfsNodeInterface | null = null + el: HTMLElement; + node: WuiVfsNodeInterface | null = null; - constructor(public onClick: NodeClickHandler) { - this.el = document.createElement("div") - this.el.classList.add(CLASS_VFS_LIST) - this.el.style.borderWidth = "1px" - this.el.style.borderStyle = "solid" - this.el.style.borderColor = "silver" - } + constructor(public onClick: NodeClickHandler) { + this.el = document.createElement("div"); + this.el.classList.add(CLASS_VFS_LIST); + this.el.style.borderWidth = "1px"; + this.el.style.borderStyle = "solid"; + this.el.style.borderColor = "silver"; + } - Open(node: WuiVfsNodeInterface) { - this.node = node - this.el.innerHTML = "" - if (!this.node.childs) { - return - } - for (let c of this.node.childs) { - let el = document.createElement("div") - el.style.padding = "1em" - el.style.cursor = "pointer" - el.innerHTML = c.name + Open(node: WuiVfsNodeInterface) { + this.node = node; + this.el.innerHTML = ""; + if (!this.node.childs) { + return; + } + for (let c of this.node.childs) { + let el = document.createElement("div"); + el.style.padding = "1em"; + el.style.cursor = "pointer"; + el.innerHTML = c.name; - if (c.is_dir) { - el.style.backgroundColor = "cornsilk" - } + if (c.is_dir) { + el.style.backgroundColor = "cornsilk"; + } - el.onclick = (ev: MouseEvent) => { - this.onClick(c) - } - el.onmouseout = (event) => { - if (c.is_dir) { - el.style.backgroundColor = "cornsilk" - } else { - el.style.backgroundColor = "white" - } - } - el.onmouseover = (event) => { - el.style.backgroundColor = "aliceblue" - } + el.onclick = (ev: MouseEvent) => { + this.onClick(c); + }; + el.onmouseout = (event) => { + if (c.is_dir) { + el.style.backgroundColor = "cornsilk"; + } else { + el.style.backgroundColor = "white"; + } + }; + el.onmouseover = (event) => { + el.style.backgroundColor = "aliceblue"; + }; - this.el.appendChild(el) - } - } + this.el.appendChild(el); + } + } } class WuiVfsPath { - el: HTMLElement - private crumbs: string[] - private onClick: PathClickHandler + el: HTMLElement; + private crumbs: string[]; + private onClick: PathClickHandler; - constructor(onClick: PathClickHandler) { - this.el = document.createElement("div") + constructor(onClick: PathClickHandler) { + this.el = document.createElement("div"); - this.el.classList.add(CLASS_VFS_PATH) - this.el.style.borderWidth = "1px" - this.el.style.borderStyle = "solid" - this.el.style.borderColor = "silver" - this.crumbs = [] - this.onClick = onClick - } + this.el.classList.add(CLASS_VFS_PATH); + this.el.style.borderWidth = "1px"; + this.el.style.borderStyle = "solid"; + this.el.style.borderColor = "silver"; + this.crumbs = []; + this.onClick = onClick; + } - Open(node: WuiVfsNodeInterface) { - this.el.innerHTML = "" - this.crumbs = [] - let paths = [] + Open(node: WuiVfsNodeInterface) { + this.el.innerHTML = ""; + this.crumbs = []; + let paths = []; - if (node.path == "/") { - paths.push(node.path) - } else { - paths = node.path.split("/") - } + if (node.path == "/") { + paths.push(node.path); + } else { + paths = node.path.split("/"); + } - for (let x = 0; x < paths.length; x++) { - let full_path = "" - let p = "" + for (let x = 0; x < paths.length; x++) { + let full_path = ""; + let p = ""; - if (x == 0) { - p = "/" - full_path = "/" - } else { - p = paths[x] - full_path = paths.slice(0, x + 1).join("/") - } + if (x == 0) { + p = "/"; + full_path = "/"; + } else { + p = paths[x]; + full_path = paths.slice(0, x + 1).join("/"); + } - let crumb = document.createElement("span") - crumb.style.display = "inline-block" - crumb.style.padding = "1em" - crumb.style.cursor = "pointer" - crumb.innerHTML = p + let crumb = document.createElement("span"); + crumb.style.display = "inline-block"; + crumb.style.padding = "1em"; + crumb.style.cursor = "pointer"; + crumb.innerHTML = p; - crumb.onclick = (event) => { - this.onClick(full_path) - } - crumb.onmouseout = (event) => { - crumb.style.backgroundColor = "white" - } - crumb.onmouseover = (event) => { - crumb.style.backgroundColor = "aliceblue" - } + crumb.onclick = (event) => { + this.onClick(full_path); + }; + crumb.onmouseout = (event) => { + crumb.style.backgroundColor = "white"; + }; + crumb.onmouseover = (event) => { + crumb.style.backgroundColor = "aliceblue"; + }; - this.el.appendChild(crumb) - } - } + this.el.appendChild(crumb); + } + } } diff --git a/websocket_client.ts b/websocket_client.ts index ded53b2..7ffdbc0 100644 --- a/websocket_client.ts +++ b/websocket_client.ts @@ -1,165 +1,159 @@ // SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info> // SPDX-License-Identifier: GPL-3.0-or-later -import { WuiResponseInterface } from "./response.js" +import { WuiResponseInterface } from "./response.js"; -const AUTO_RECONNECT_INTERVAL = 5000 +const AUTO_RECONNECT_INTERVAL = 5000; interface RequestQueue { - req: WuiWebSocketRequest - cbSuccess: (res: WuiWebSocketResponse) => void - cbFail: (err: string) => void + req: WuiWebSocketRequest; + cbSuccess: (res: WuiWebSocketResponse) => void; + cbFail: (err: string) => void; } export interface WuiWebSocketOptions { - address: string - auto_reconnect: boolean // If true the client will handle auto-reconnect. - auto_reconnect_interval: number // The interval for auto-reconnect, default to 5 seconds. - onBroadcast: (res: WuiWebSocketResponse) => void - onConnected: () => void - onDisconnected: () => void - onError: () => void + address: string; + auto_reconnect: boolean; // If true the client will handle auto-reconnect. + auto_reconnect_interval: number; // The interval for auto-reconnect, default to 5 seconds. + onBroadcast: (res: WuiWebSocketResponse) => void; + onConnected: () => void; + onDisconnected: () => void; + onError: () => void; } export interface WuiWebSocketRequest { - id: number - method: string - target: string - body?: string + id: number; + method: string; + target: string; + body?: string; } export interface WuiWebSocketResponse { - id: number - code: number - message: string - body: string + id: number; + code: number; + message: string; + body: string; } export class WuiWebSocketClient { - address: string - conn!: WebSocket - requestQueue: RequestQueue[] = [] - reconnect_id: number = 0 - isOpen: boolean = false - error: string = "" + address: string; + conn!: WebSocket; + requestQueue: RequestQueue[] = []; + reconnect_id: number = 0; + isOpen: boolean = false; + error: string = ""; - constructor(public opts: WuiWebSocketOptions) { - this.address = opts.address - if (opts.auto_reconnect) { - if (opts.auto_reconnect_interval <= 0) { - opts.auto_reconnect_interval = - AUTO_RECONNECT_INTERVAL - } - } - this.connect() - } + constructor(public opts: WuiWebSocketOptions) { + this.address = opts.address; + if (opts.auto_reconnect) { + if (opts.auto_reconnect_interval <= 0) { + opts.auto_reconnect_interval = AUTO_RECONNECT_INTERVAL; + } + } + this.connect(); + } - // - // Send the request and wait for response similar to HTTP - // request-response. - // - async Send(req: WuiWebSocketRequest): Promise<WuiResponseInterface> { - return new Promise((resolve, reject) => { - let wuiRes: WuiResponseInterface = { - code: 0, - message: "", - } - let reqQueue: RequestQueue = { - req: req, - cbSuccess: (res: WuiWebSocketResponse) => { - wuiRes.code = res.code - wuiRes.message = res.message - if ( - res.code === 200 && - res.body.length > 0 - ) { - wuiRes.data = JSON.parse( - atob(res.body), - ) - } - resolve(wuiRes) - }, - cbFail: (err: string) => { - wuiRes.code = 500 - wuiRes.message = err - resolve(wuiRes) - }, - } - this.requestQueue.push(reqQueue) - this.conn.send(JSON.stringify(req)) - }) - } + // + // Send the request and wait for response similar to HTTP + // request-response. + // + async Send(req: WuiWebSocketRequest): Promise<WuiResponseInterface> { + return new Promise((resolve, reject) => { + let wuiRes: WuiResponseInterface = { + code: 0, + message: "", + }; + let reqQueue: RequestQueue = { + req: req, + cbSuccess: (res: WuiWebSocketResponse) => { + wuiRes.code = res.code; + wuiRes.message = res.message; + if (res.code === 200 && res.body.length > 0) { + wuiRes.data = JSON.parse(atob(res.body)); + } + resolve(wuiRes); + }, + cbFail: (err: string) => { + wuiRes.code = 500; + wuiRes.message = err; + resolve(wuiRes); + }, + }; + this.requestQueue.push(reqQueue); + this.conn.send(JSON.stringify(req)); + }); + } - connect() { - this.conn = new WebSocket(this.address) + connect() { + this.conn = new WebSocket(this.address); - this.conn.onclose = (ev: CloseEvent) => { - this.onClose(ev) - } - this.conn.onerror = (ev: Event) => { - this.onError(ev) - } - this.conn.onmessage = (ev: MessageEvent) => { - this.onMessage(ev) - } - this.conn.onopen = (ev: Event) => { - this.onOpen(ev) - } - } + this.conn.onclose = (ev: CloseEvent) => { + this.onClose(ev); + }; + this.conn.onerror = (ev: Event) => { + this.onError(ev); + }; + this.conn.onmessage = (ev: MessageEvent) => { + this.onMessage(ev); + }; + this.conn.onopen = (ev: Event) => { + this.onOpen(ev); + }; + } - // onClose handle connection closed by cleaning up the request - // queue. - onClose(ev: CloseEvent) { - for (let x = 0; x < this.requestQueue.length; x++) { - this.requestQueue[x].cbFail("connection closed") - } + // onClose handle connection closed by cleaning up the request + // queue. + onClose(ev: CloseEvent) { + for (let x = 0; x < this.requestQueue.length; x++) { + this.requestQueue[x].cbFail("connection closed"); + } - this.isOpen = false - this.error = "connection is closed by server" + this.isOpen = false; + this.error = "connection is closed by server"; - if (this.opts.auto_reconnect && !this.reconnect_id) { - this.reconnect_id = setInterval(() => { - this.connect() - }, this.opts.auto_reconnect_interval) - } - if (this.opts.onDisconnected) { - this.opts.onDisconnected() - } - } + if (this.opts.auto_reconnect && !this.reconnect_id) { + this.reconnect_id = setInterval(() => { + this.connect(); + }, this.opts.auto_reconnect_interval); + } + if (this.opts.onDisconnected) { + this.opts.onDisconnected(); + } + } - onError(ev: Event) { - if (this.opts.onError) { - this.opts.onError() - } - } + onError(ev: Event) { + if (this.opts.onError) { + this.opts.onError(); + } + } - onMessage(ev: MessageEvent) { - let res: WuiWebSocketResponse = JSON.parse(ev.data) + onMessage(ev: MessageEvent) { + let res: WuiWebSocketResponse = JSON.parse(ev.data); - for (let x = 0; x < this.requestQueue.length; x++) { - let reqq = this.requestQueue[x] - if (reqq.req.id === res.id) { - reqq.cbSuccess(res) - this.requestQueue.splice(x, 1) - return - } - } + for (let x = 0; x < this.requestQueue.length; x++) { + let reqq = this.requestQueue[x]; + if (reqq.req.id === res.id) { + reqq.cbSuccess(res); + this.requestQueue.splice(x, 1); + return; + } + } - if (this.opts.onBroadcast && res.id == 0) { - this.opts.onBroadcast(res) - } - } + if (this.opts.onBroadcast && res.id == 0) { + this.opts.onBroadcast(res); + } + } - onOpen(ev: Event) { - this.isOpen = true - this.error = "" + onOpen(ev: Event) { + this.isOpen = true; + this.error = ""; - if (this.reconnect_id) { - clearInterval(this.reconnect_id) - this.reconnect_id = 0 - } - if (this.opts.onConnected) { - this.opts.onConnected() - } - } + if (this.reconnect_id) { + clearInterval(this.reconnect_id); + this.reconnect_id = 0; + } + if (this.opts.onConnected) { + this.opts.onConnected(); + } + } } |
