Implement fields having multiple items in creator

This commit is contained in:
Reimar 2026-01-19 09:39:51 +01:00
parent 8730b7c199
commit dbde3a5834
Signed by: Reimar
GPG Key ID: 93549FA07F0AE268
8 changed files with 150 additions and 28 deletions

View File

@ -10,6 +10,7 @@ export class Field {
categoryName = null; categoryName = null;
isHidden = false; isHidden = false;
isDisabled = false; isDisabled = false;
allowMultiple = false;
constructor(key) { constructor(key) {
this.key = key; this.key = key;
@ -55,4 +56,9 @@ export class Field {
this.isDisabled = true; this.isDisabled = true;
return this; return this;
} }
multiple() {
this.allowMultiple = true;
return this;
}
} }

View File

@ -1,29 +1,66 @@
import { FieldInputItem } from "./FieldInputItem.js";
/** /**
* Represents the actual input element on the creator tool * Represents the actual input element on the creator tool
* A field may have multiple inputs if it allows multiple values * May contain multiple items if the field allows multiple values
*/ */
export class FieldInput { export class FieldInput {
constructor(field, parentElem) { constructor(field, parentElem) {
this.field = field; this.field = field;
this.id = "field-" + field.key; this.id = "field-" + field.key;
this.items = [];
this.parent = parentElem;
if (!field.isHidden) this.appendHtml(parentElem);
parentElem.innerHTML += `
<label for="${field.key}">${field.displayName}</label>
<p class="description">${field.description ?? ""}</p>
${field.getInputHtml(this.id)}
`;
} }
isValid() { appendHtml(parent) {
return this.field.isValidInput(this.id); const fieldContainer = document.createElement("div");
fieldContainer.className = "field-container";
const item = new FieldInputItem(this, fieldContainer);
if (!this.field.isHidden) {
parent.insertAdjacentHTML("beforeend", `
<label for="${this.field.key}">${this.field.displayName}</label>
<p class="description">${this.field.description ?? ""}</p>
`);
parent.appendChild(fieldContainer);
if (this.field.allowMultiple && this.items.length === 0) {
const addBtn = document.createElement("button");
addBtn.className = "text-button";
addBtn.type = "button";
addBtn.innerText = "+ Add";
addBtn.onclick = () => this.addItem(fieldContainer);
parent.appendChild(addBtn);
}
}
this.items.push(item);
this.updateItems();
} }
getValue() { updateItems() {
return this.field.getInputValue(this.id); for (const item of this.items) item.update();
} }
toString() { addItem(parent) {
return this.field.inputToString(this.id); const item = new FieldInputItem(this, parent);
this.items.push(item);
this.updateItems();
}
removeItem(id) {
const i = this.items.findIndex(item => item.id === id);
this.items[i].remove();
this.items.splice(i, 1);
this.updateItems();
this.parent.closest("form").onchange();
} }
} }

View File

@ -0,0 +1,60 @@
export class FieldInputItem {
container = null;
removeBtn = null;
constructor(input, parentElem) {
this.input = input;
this.field = input.field;
this.id = input.id + (input.field.allowMultiple ? "-" + Math.random().toString().slice(2, 5) : "");
this.appendHtml(parentElem);
}
appendHtml(parent) {
const container = document.createElement("div");
container.style.display = "flex";
container.style.gap = "0.5rem";
if (this.field.allowMultiple) {
const wrapper = document.createElement("div");
wrapper.innerHTML = this.field.getInputHtml(this.id);
container.appendChild(wrapper);
const removeBtn = document.createElement("button");
removeBtn.type = "button";
removeBtn.className = "text-button";
removeBtn.innerHTML = "&times; Remove";
removeBtn.onclick = () => this.input.removeItem(this.id);
container.appendChild(removeBtn);
this.removeBtn = removeBtn;
} else {
container.innerHTML = this.field.getInputHtml(this.id);
}
parent.appendChild(container);
this.container = container;
}
update() {
if (this.removeBtn) {
this.removeBtn.style.display = this.input.items.length > 1 ? "inline-block" : "none";
}
}
remove() {
this.container.remove();
}
isValid() {
return this.field.isValidInput(this.id);
}
getValue() {
return this.field.getInputValue(this.id);
}
toString() {
return this.field.inputToString(this.id);
}
}

View File

@ -86,10 +86,10 @@ export class SpfRecord {
this.text = text; this.text = text;
} }
static createFromFieldInputs(fieldInputs) { static createFromFieldInputItems(items) {
const tokens = fieldInputs const tokens = items
.filter(input => !input.field.isDisabled && input.isValid()) .filter(item => !item.field.isDisabled && item.isValid())
.map(input => input.toString()); .map(item => item.toString());
const text = tokens.join(" "); const text = tokens.join(" ");

View File

@ -8,10 +8,10 @@ export class TagListRecord {
this.text = text; this.text = text;
} }
static createFromFieldInputs(fieldInputs) { static createFromFieldInputItems(items) {
const tokens = fieldInputs const tokens = items
.filter(input => !input.field.isDisabled && input.isValid()) .filter(item => !item.field.isDisabled && item.isValid())
.filter(input => !input.field.defaultValue || input.getValue() !== input.field.defaultValue) .filter(item => !item.field.defaultValue || item.getValue() !== item.field.defaultValue)
.map(input => input.toString()); .map(input => input.toString());
const text = tokens.join("; "); const text = tokens.join("; ");

View File

@ -5,7 +5,6 @@ export class Term extends Field {
separator = null; separator = null;
isRequired = false; isRequired = false;
position = null; position = null;
allowMultiple = false;
valueRequirement = ValueRequirement.REQUIRED; valueRequirement = ValueRequirement.REQUIRED;
constructor(key) { constructor(key) {
@ -52,11 +51,6 @@ export class Term extends Field {
return this; return this;
} }
multiple() {
this.allowMultiple = true;
return this;
}
value(requirement) { value(requirement) {
this.valueRequirement = requirement; this.valueRequirement = requirement;
return this; return this;

View File

@ -36,7 +36,12 @@ document.getElementById("form").onchange = generate;
generate(); generate();
function generate() { function generate() {
const record = Record.createFromFieldInputs(inputs); const items = [];
for (const input of inputs) {
items.push(...input.items);
}
const record = Record.createFromFieldInputItems(items);
document.getElementById("record").value = record.text; document.getElementById("record").value = record.text;
} }

View File

@ -164,3 +164,23 @@ summary {
summary:hover { summary:hover {
color: black; color: black;
} }
.field-container {
display: flex;
flex-direction: column;
align-items: start;
gap: 0.5rem;
margin-bottom: 0.5rem;
}
.text-button {
background: none;
border: none;
cursor: pointer;
color: #757575;
transition: color 200ms;
}
.text-button:hover {
color: black;
}