Implement fields having multiple items in creator
This commit is contained in:
parent
8730b7c199
commit
dbde3a5834
@ -10,6 +10,7 @@ export class Field {
|
||||
categoryName = null;
|
||||
isHidden = false;
|
||||
isDisabled = false;
|
||||
allowMultiple = false;
|
||||
|
||||
constructor(key) {
|
||||
this.key = key;
|
||||
@ -55,4 +56,9 @@ export class Field {
|
||||
this.isDisabled = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
multiple() {
|
||||
this.allowMultiple = true;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,29 +1,66 @@
|
||||
import { FieldInputItem } from "./FieldInputItem.js";
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
constructor(field, parentElem) {
|
||||
this.field = field;
|
||||
this.id = "field-" + field.key;
|
||||
this.items = [];
|
||||
this.parent = parentElem;
|
||||
|
||||
if (!field.isHidden)
|
||||
parentElem.innerHTML += `
|
||||
<label for="${field.key}">${field.displayName}</label>
|
||||
<p class="description">${field.description ?? ""}</p>
|
||||
${field.getInputHtml(this.id)}
|
||||
`;
|
||||
this.appendHtml(parentElem);
|
||||
}
|
||||
|
||||
isValid() {
|
||||
return this.field.isValidInput(this.id);
|
||||
appendHtml(parent) {
|
||||
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() {
|
||||
return this.field.getInputValue(this.id);
|
||||
updateItems() {
|
||||
for (const item of this.items) item.update();
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.field.inputToString(this.id);
|
||||
addItem(parent) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
60
assets/scripts/FieldInputItem.js
Normal file
60
assets/scripts/FieldInputItem.js
Normal 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 = "× 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);
|
||||
}
|
||||
}
|
||||
@ -86,10 +86,10 @@ export class SpfRecord {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
static createFromFieldInputs(fieldInputs) {
|
||||
const tokens = fieldInputs
|
||||
.filter(input => !input.field.isDisabled && input.isValid())
|
||||
.map(input => input.toString());
|
||||
static createFromFieldInputItems(items) {
|
||||
const tokens = items
|
||||
.filter(item => !item.field.isDisabled && item.isValid())
|
||||
.map(item => item.toString());
|
||||
|
||||
const text = tokens.join(" ");
|
||||
|
||||
|
||||
@ -8,10 +8,10 @@ export class TagListRecord {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
static createFromFieldInputs(fieldInputs) {
|
||||
const tokens = fieldInputs
|
||||
.filter(input => !input.field.isDisabled && input.isValid())
|
||||
.filter(input => !input.field.defaultValue || input.getValue() !== input.field.defaultValue)
|
||||
static createFromFieldInputItems(items) {
|
||||
const tokens = items
|
||||
.filter(item => !item.field.isDisabled && item.isValid())
|
||||
.filter(item => !item.field.defaultValue || item.getValue() !== item.field.defaultValue)
|
||||
.map(input => input.toString());
|
||||
|
||||
const text = tokens.join("; ");
|
||||
|
||||
@ -5,7 +5,6 @@ export class Term extends Field {
|
||||
separator = null;
|
||||
isRequired = false;
|
||||
position = null;
|
||||
allowMultiple = false;
|
||||
valueRequirement = ValueRequirement.REQUIRED;
|
||||
|
||||
constructor(key) {
|
||||
@ -52,11 +51,6 @@ export class Term extends Field {
|
||||
return this;
|
||||
}
|
||||
|
||||
multiple() {
|
||||
this.allowMultiple = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
value(requirement) {
|
||||
this.valueRequirement = requirement;
|
||||
return this;
|
||||
|
||||
@ -36,7 +36,12 @@ document.getElementById("form").onchange = generate;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -164,3 +164,23 @@ summary {
|
||||
summary:hover {
|
||||
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;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user