aboutsummaryrefslogtreecommitdiff
path: root/static/ts/components/input-list.ts
diff options
context:
space:
mode:
Diffstat (limited to 'static/ts/components/input-list.ts')
-rw-r--r--static/ts/components/input-list.ts70
1 files changed, 59 insertions, 11 deletions
diff --git a/static/ts/components/input-list.ts b/static/ts/components/input-list.ts
index 31dd5158..72d27cab 100644
--- a/static/ts/components/input-list.ts
+++ b/static/ts/components/input-list.ts
@@ -14,26 +14,62 @@ export { InputList }
TODO allow each item to be a larger unit, possibly containing multiple input
fields.
*/
+/**
+ A multi-valued input, done by creating extra input fields as needed.
+
+ The first element of body MUST be an input element, which will be used as the
+ template for each instance. A tag input could for example look like
+
+ @example
+ ```html
+ <input-list name="tags">
+ <input type="text" placeholder="tag ..." />
+ </input-list>
+ ```
+
+ Whenever one of the input elements `value` becomes the empty string, that tag
+ is removed, and whenever there is no element with the empty string as a
+ `value`, a new input element will be added onto the end.
+ */
class InputList extends HTMLElement {
- el: HTMLInputElement;
+ /** The element used as our template. Will be sourced from the initial HTML code. */
+ #el: HTMLInputElement;
+ /**
+ Registered listeners, which will be added onto each created entry
+
+ Keys are event names ('input', 'change', ...) and values event handlers.
+
+ This is a list of tuples rather than a dictionary, since multiple
+ listeners of the same type can be registered.
+ */
#listeners: [string, (e: Event) => void][] = [];
constructor() {
super();
- this.el = this.children[0].cloneNode(true) as HTMLInputElement;
+ this.#el = this.children[0].cloneNode(true) as HTMLInputElement;
}
+ /** Clears all existing children upon mount */
connectedCallback() {
for (let child of this.children) {
child.remove();
}
- this.addInstance();
+ this.#addInstance();
}
- createInstance(): HTMLInputElement {
- let new_el = this.el.cloneNode(true) as HTMLInputElement
+ /**
+ Instanciates a new instance of the input element.
+
+ An event listener for 'input' will be added, which will handle the
+ addition and removing of other elements.
+
+ All event listeners attachet on the input-list component will also be
+ added.
+ */
+ #createInstance(): HTMLInputElement {
+ let new_el = this.#el.cloneNode(true) as HTMLInputElement
let that = this;
new_el.addEventListener('input', function() {
/* TODO .value is empty both if it's actually empty, but also
@@ -49,7 +85,7 @@ class InputList extends HTMLElement {
}
} else {
if (!this.nextElementSibling) {
- that.addInstance();
+ that.#addInstance();
// window.setTimeout(() => this.focus())
this.focus();
}
@@ -63,8 +99,9 @@ class InputList extends HTMLElement {
return new_el;
}
- addInstance() {
- let new_el = this.createInstance();
+ /** Add a new instance of the input element to the container */
+ #addInstance() {
+ let new_el = this.#createInstance();
this.appendChild(new_el);
}
@@ -72,7 +109,7 @@ class InputList extends HTMLElement {
* The value from each element, except the last which should always be empty.
* Has an unspecified type, since children:s value field might give non-strings.
*/
- get value(): any[] {
+ get value(): unknown[] {
let value_list = []
for (let child of this.children) {
value_list.push((child as any).value);
@@ -83,6 +120,12 @@ class InputList extends HTMLElement {
return value_list
}
+ /**
+ Overwrite the current value with a new one.
+
+ Each entry in the array will be mapped unto one instance of the template
+ input element. A final empty element will also be added.
+ */
set value(new_value: any[]) {
let all_equal = true;
@@ -111,17 +154,22 @@ class InputList extends HTMLElement {
/* clear dictionary */
values.set(value, false);
} else {
- let new_el = this.createInstance();
+ let new_el = this.#createInstance();
new_el.value = value;
output_list.push(new_el);
}
}
/* final, trailing, element */
- output_list.push(this.createInstance());
+ output_list.push(this.#createInstance());
this.replaceChildren(...output_list);
}
+ /**
+ Add an event listener to each of the inputs.
+
+ This basically works as the "regular" version.
+ */
addEventListener(type: string, proc: ((e: Event) => void)) {
// if (type != 'input') throw "Only input supported";