Refactor forms.js and generate custom form elements in the template.
This commit is contained in:
parent
731e69a80b
commit
290bb67293
4 changed files with 501 additions and 166 deletions
|
@ -374,6 +374,9 @@ body {
|
|||
column-gap: 10px;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
#content-edit-profile form .form-sub-entry.hidden {
|
||||
display: none;
|
||||
}
|
||||
#content-edit-profile form .form-sub-entry :nth-child(1) {
|
||||
grid-area: type-input;
|
||||
}
|
||||
|
@ -393,6 +396,12 @@ body {
|
|||
#content-edit-profile form .form-sub-entry .remove-button:active {
|
||||
background-color: #973232;
|
||||
}
|
||||
#content-edit-profile form .form-sub-entry input[type=checkbox] {
|
||||
display: none;
|
||||
}
|
||||
#content-edit-profile form .form-sub-entry-template {
|
||||
display: none;
|
||||
}
|
||||
#content-edit-profile form .add-sub-entry-button {
|
||||
margin: 0 auto 0 auto;
|
||||
}
|
||||
|
|
|
@ -1,80 +1,90 @@
|
|||
"use strict";
|
||||
|
||||
function countSubEntries(formEntryElement) {
|
||||
return formEntryElement
|
||||
.querySelectorAll(".form-sub-entry")
|
||||
.length;
|
||||
}
|
||||
// function countSubEntries(formEntryElement) {
|
||||
// return formEntryElement
|
||||
// .querySelectorAll(".form-sub-entry")
|
||||
// .length;
|
||||
// }
|
||||
|
||||
function reindexSubEntries(formEntryElement) {
|
||||
const subEntryElements = [...formEntryElement.querySelectorAll(".form-sub-entry")];
|
||||
for (let [subEntryIndex, subEntryElement] of subEntryElements.entries()) {
|
||||
subEntryElement.setAttribute("data-sub-entry-index", subEntryIndex);
|
||||
}
|
||||
}
|
||||
// function reindexSubEntries(formEntryElement) {
|
||||
// const subEntryElements = [...formEntryElement.querySelectorAll(".form-sub-entry")];
|
||||
// for (let [subEntryIndex, subEntryElement] of subEntryElements.entries()) {
|
||||
// subEntryElement.setAttribute("data-sub-entry-index", subEntryIndex);
|
||||
// }
|
||||
// }
|
||||
|
||||
function createAndAppendNewSubEntryButton(formEntryElement) {
|
||||
const buttonElement = document.createElement("button");
|
||||
buttonElement.setAttribute("type", "button");
|
||||
buttonElement.classList.add("add-sub-entry-button");
|
||||
// function checkSubEntryDeleteCheckbox(subEntryElement) {
|
||||
// const checkboxElement = [...subEntryElement.querySelectorAll('input[type="checkbox"')]
|
||||
// .filter(element => element.className.endsWith("DELETE"));
|
||||
|
||||
// if (checkboxElement.length >= 1) {
|
||||
// checkboxElement.checked = true;
|
||||
// }
|
||||
// }
|
||||
|
||||
// TODO: handle the translation of this button
|
||||
buttonElement.textContent = "New entry";
|
||||
|
||||
buttonElement.addEventListener("click", event => {
|
||||
// Clone one of the sub-entries, make it look like it is new, and reindex the sub-entries
|
||||
// This works because there is always at least one sub-entry to clone
|
||||
const someSubEntryElement = formEntryElement.querySelector(".form-sub-entry");
|
||||
const clonedSubEntryElement = someSubEntryElement.cloneNode(true);
|
||||
// function createAndAppendNewSubEntryButton(formEntryElement) {
|
||||
// const buttonElement = document.createElement("button");
|
||||
// buttonElement.setAttribute("type", "button");
|
||||
// buttonElement.classList.add("add-sub-entry-button");
|
||||
|
||||
for (let inputElement of clonedSubEntryElement.querySelectorAll("input")) {
|
||||
inputElement.value = "";
|
||||
}
|
||||
// // TODO: handle the translation of this button
|
||||
// buttonElement.textContent = "New entry";
|
||||
|
||||
// Since the cloned removal button has lost its click event listener,
|
||||
// it should be created again from scratch
|
||||
clonedSubEntryElement
|
||||
.querySelector(".remove-button")
|
||||
.remove();
|
||||
createAndAppendSubEntryDeletionButton(clonedSubEntryElement);
|
||||
// buttonElement.addEventListener("click", event => {
|
||||
// // Clone one of the sub-entries, make it look like it is new, and reindex the sub-entries
|
||||
// // This works because there is always at least one sub-entry to clone
|
||||
// const someSubEntryElement = formEntryElement.querySelector(".form-sub-entry");
|
||||
// const clonedSubEntryElement = someSubEntryElement.cloneNode(true);
|
||||
|
||||
formEntryElement.insertBefore(clonedSubEntryElement, buttonElement);
|
||||
reindexSubEntries(formEntryElement);
|
||||
});
|
||||
// for (let inputElement of clonedSubEntryElement.querySelectorAll("input")) {
|
||||
// inputElement.value = "";
|
||||
// }
|
||||
|
||||
formEntryElement.append(buttonElement);
|
||||
}
|
||||
// // Since the cloned removal button has lost its click event listener,
|
||||
// // it should be created again from scratch
|
||||
// clonedSubEntryElement
|
||||
// .querySelector(".remove-button")
|
||||
// .remove();
|
||||
// createAndAppendSubEntryDeletionButton(clonedSubEntryElement);
|
||||
|
||||
function createAndAppendSubEntryDeletionButton(subEntryElement) {
|
||||
const buttonElement = document.createElement("button");
|
||||
buttonElement.setAttribute("type", "button");
|
||||
buttonElement.classList.add("remove-button");
|
||||
// formEntryElement.insertBefore(clonedSubEntryElement, buttonElement);
|
||||
// reindexSubEntries(formEntryElement);
|
||||
// });
|
||||
|
||||
buttonElement.addEventListener("click", event => {
|
||||
const parentFormEntryElement = subEntryElement.parentNode;
|
||||
// formEntryElement.append(buttonElement);
|
||||
// }
|
||||
|
||||
// If this sub-entry is the last one, only clear its input fields
|
||||
// Otherwise, remove the node from the form entry
|
||||
if (countSubEntries(parentFormEntryElement) === 1) {
|
||||
subEntryElement
|
||||
.querySelectorAll("input")
|
||||
.value = "";
|
||||
}
|
||||
else {
|
||||
subEntryElement.remove();
|
||||
reindexSubEntries(parentFormEntryElement);
|
||||
}
|
||||
});
|
||||
// function createAndAppendSubEntryDeletionButton(subEntryElement) {
|
||||
// const buttonElement = document.createElement("button");
|
||||
// buttonElement.setAttribute("type", "button");
|
||||
// buttonElement.classList.add("remove-button");
|
||||
|
||||
subEntryElement.append(buttonElement);
|
||||
}
|
||||
// buttonElement.addEventListener("click", event => {
|
||||
// const parentFormEntryElement = subEntryElement.parentNode;
|
||||
|
||||
function createSubEntryElement() {
|
||||
const subEntryElement = document.createElement("div");
|
||||
subEntryElement.classList.add("form-sub-entry");
|
||||
// // If this sub-entry is the last one, only clear its input fields
|
||||
// // Otherwise, remove the node from the form entry
|
||||
// if (countSubEntries(parentFormEntryElement) === 1) {
|
||||
// subEntryElement
|
||||
// .querySelectorAll("input")
|
||||
// .value = "";
|
||||
// }
|
||||
// else {
|
||||
// subEntryElement.remove();
|
||||
// reindexSubEntries(parentFormEntryElement);
|
||||
// }
|
||||
// });
|
||||
|
||||
return subEntryElement;
|
||||
}
|
||||
// subEntryElement.append(buttonElement);
|
||||
// }
|
||||
|
||||
// function createSubEntryElement() {
|
||||
// const subEntryElement = document.createElement("div");
|
||||
// subEntryElement.classList.add("form-sub-entry");
|
||||
|
||||
// return subEntryElement;
|
||||
// }
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -82,117 +92,341 @@ function createSubEntryElement() {
|
|||
|
||||
// TODO: remove this hacky function by directly grouping all the elements of each sub-entry
|
||||
// directly in Django (e.g. <div> elements with a "form-sub-entry" class)
|
||||
function transformFormEntryWithSubEntries(formEntryElement) {
|
||||
// Nb. successive children to group together
|
||||
// (6 - 4 = 2, since the checkbox and all the label are ignored)
|
||||
const nbSuccessiveElementsPerSubEntry = 2;
|
||||
// function transformFormEntryWithSubEntries(formEntryElement) {
|
||||
// // Nb. successive children to group together
|
||||
// // (6 - 4 = 2, since the checkbox and all the label are ignored)
|
||||
// const nbSuccessiveElementsPerSubEntry = 2;
|
||||
|
||||
const subEntryElements = [];
|
||||
let currentSubEntryElement = createSubEntryElement();
|
||||
let nbChildrenOfCurrentSubEntry = 0;
|
||||
subEntryElements.push(currentSubEntryElement);
|
||||
// const subEntryElements = [];
|
||||
// let currentSubEntryElement = createSubEntryElement();
|
||||
// let nbChildrenOfCurrentSubEntry = 0;
|
||||
// subEntryElements.push(currentSubEntryElement);
|
||||
|
||||
const formEntryChildNodes = [...formEntryElement.childNodes];
|
||||
let firstElementHasBeenSkipped = false;
|
||||
// const formEntryChildNodes = [...formEntryElement.childNodes];
|
||||
// let firstElementHasBeenSkipped = false;
|
||||
|
||||
for (let childNode of formEntryChildNodes) {
|
||||
// Ignore non-element nodes
|
||||
if (childNode.nodeType !== Node.ELEMENT_NODE) {
|
||||
continue;
|
||||
}
|
||||
// for (let childNode of formEntryChildNodes) {
|
||||
// // Ignore non-element nodes
|
||||
// if (childNode.nodeType !== Node.ELEMENT_NODE) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// Skip the first element (it should be the label of the whole form entry)
|
||||
if (!firstElementHasBeenSkipped) {
|
||||
firstElementHasBeenSkipped = true;
|
||||
continue;
|
||||
}
|
||||
// // Skip the first element (it should be the label of the whole form entry)
|
||||
// if (!firstElementHasBeenSkipped) {
|
||||
// firstElementHasBeenSkipped = true;
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// Ignore hidden form elements
|
||||
if (childNode.hasAttribute("type")
|
||||
&& childNode.getAttribute("type") === "hidden") {
|
||||
continue;
|
||||
}
|
||||
// // Ignore hidden form elements
|
||||
// if (childNode.hasAttribute("type")
|
||||
// && childNode.getAttribute("type") === "hidden") {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// Remove the checkbox to delete an entry and its label
|
||||
if (childNode.hasAttribute("id")
|
||||
&& childNode.getAttribute("id").endsWith("DELETE")) {
|
||||
childNode.remove();
|
||||
continue;
|
||||
}
|
||||
// // Remove the checkbox to delete an entry and its label
|
||||
// if (childNode.hasAttribute("id")
|
||||
// && childNode.getAttribute("id").endsWith("DELETE")) {
|
||||
// //childNode.remove();
|
||||
// continue;
|
||||
// }
|
||||
|
||||
if (childNode.hasAttribute("for")
|
||||
&& childNode.getAttribute("for").endsWith("DELETE")) {
|
||||
childNode.remove();
|
||||
continue;
|
||||
}
|
||||
// if (childNode.hasAttribute("for")
|
||||
// && childNode.getAttribute("for").endsWith("DELETE")) {
|
||||
// //childNode.remove();
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// Remove all the remaining labels
|
||||
// (they should be replaced by placeholder text within the input)
|
||||
if (childNode.nodeName === "LABEL") {
|
||||
childNode.remove();
|
||||
continue;
|
||||
}
|
||||
// // Remove all the remaining labels
|
||||
// // (they should be replaced by placeholder text within the input)
|
||||
// if (childNode.nodeName === "LABEL") {
|
||||
// childNode.remove();
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// If the current sub-entry is full,
|
||||
// create a new sub-entry (for the remaining child nodes)
|
||||
if (nbChildrenOfCurrentSubEntry === nbSuccessiveElementsPerSubEntry) {
|
||||
currentSubEntryElement = createSubEntryElement();
|
||||
nbChildrenOfCurrentSubEntry = 0;
|
||||
subEntryElements.push(currentSubEntryElement);
|
||||
}
|
||||
// // If the current sub-entry is full,
|
||||
// // create a new sub-entry (for the remaining child nodes)
|
||||
// if (nbChildrenOfCurrentSubEntry === nbSuccessiveElementsPerSubEntry) {
|
||||
// currentSubEntryElement = createSubEntryElement();
|
||||
// nbChildrenOfCurrentSubEntry = 0;
|
||||
// subEntryElements.push(currentSubEntryElement);
|
||||
// }
|
||||
|
||||
// Fill the current sub-entry (until it becomes full)
|
||||
// Note: the order MUST be preserved for the CSS to work (see _content.scss for details)
|
||||
currentSubEntryElement.append(childNode);
|
||||
nbChildrenOfCurrentSubEntry += 1;
|
||||
}
|
||||
// // Fill the current sub-entry (until it becomes full)
|
||||
// // Note: the order MUST be preserved for the CSS to work (see _content.scss for details)
|
||||
// currentSubEntryElement.append(childNode);
|
||||
// nbChildrenOfCurrentSubEntry += 1;
|
||||
// }
|
||||
|
||||
// For each sub-entry element:
|
||||
// - add placeholder text in each input field
|
||||
// - add a button to remove the sub-entry
|
||||
// Note: this method is not robust since it assumes the two elements always exist
|
||||
// and it identifies them by their relative order only!
|
||||
for (let [subEntryIndex, subEntryElement] of subEntryElements.entries()) {
|
||||
const subEntryInputElements = subEntryElement.querySelectorAll("input");
|
||||
subEntryInputElements[0].setAttribute("placeholder", "Type"); // TODO: handle translation
|
||||
subEntryInputElements[1].setAttribute("placeholder", "Value"); // TODO: handle translation
|
||||
// // For each sub-entry element:
|
||||
// // - add placeholder text in each input field
|
||||
// // - add a button to remove the sub-entry
|
||||
// // Note: this method is not robust since it assumes the two elements always exist
|
||||
// // and it identifies them by their relative order only!
|
||||
// for (let [subEntryIndex, subEntryElement] of subEntryElements.entries()) {
|
||||
// const subEntryInputElements = subEntryElement.querySelectorAll("input");
|
||||
// subEntryInputElements[0].setAttribute("placeholder", "Type"); // TODO: handle translation
|
||||
// subEntryInputElements[1].setAttribute("placeholder", "Value"); // TODO: handle translation
|
||||
|
||||
createAndAppendSubEntryDeletionButton(subEntryElement);
|
||||
}
|
||||
// createAndAppendSubEntryDeletionButton(subEntryElement);
|
||||
// }
|
||||
|
||||
// Finally append all the sub-entry elements to the form entry
|
||||
formEntryElement.append(...subEntryElements);
|
||||
reindexSubEntries(formEntryElement);
|
||||
}
|
||||
// // Finally append all the sub-entry elements to the form entry
|
||||
// formEntryElement.append(...subEntryElements);
|
||||
// reindexSubEntries(formEntryElement);
|
||||
// }
|
||||
|
||||
// TODO: remove this hacky setup (cf. the TODO above)
|
||||
document.addEventListener("DOMContentLoaded", event => {
|
||||
// Create a list of all the form entry elements which contain sub entries
|
||||
// by filtering the list of all the form entry elements
|
||||
const targetForAttributeValues = [
|
||||
"id_phone",
|
||||
"id_social",
|
||||
"id_mail",
|
||||
"id_address"
|
||||
];
|
||||
// document.addEventListener("DOMContentLoaded", event => {
|
||||
// // Create a list of all the form entry elements which contain sub entries
|
||||
// // by filtering the list of all the form entry elements
|
||||
// const targetForAttributeValues = [
|
||||
// "id_phone",
|
||||
// "id_social",
|
||||
// "id_mail",
|
||||
// "id_address"
|
||||
// ];
|
||||
|
||||
const formEntryElementsWithSubEntries = [...document.querySelectorAll("#content-edit-profile .form-entry")]
|
||||
.filter(formEntryElement => {
|
||||
return [...formEntryElement.childNodes]
|
||||
.some(formEntryChildElement => {
|
||||
return formEntryChildElement.nodeType === Node.ELEMENT_NODE
|
||||
&& formEntryChildElement.hasAttribute("for")
|
||||
&& targetForAttributeValues.includes(formEntryChildElement.getAttribute("for"))
|
||||
});
|
||||
});
|
||||
// const formEntryElementsWithSubEntries = [...document.querySelectorAll("#content-edit-profile .form-entry")]
|
||||
// .filter(formEntryElement => {
|
||||
// return [...formEntryElement.childNodes]
|
||||
// .some(formEntryChildElement => {
|
||||
// return formEntryChildElement.nodeType === Node.ELEMENT_NODE
|
||||
// && formEntryChildElement.hasAttribute("for")
|
||||
// && targetForAttributeValues.includes(formEntryChildElement.getAttribute("for"))
|
||||
// });
|
||||
// });
|
||||
|
||||
// Transform the remaining form entry elements to fix their structure
|
||||
for (let formEntryElement of formEntryElementsWithSubEntries) {
|
||||
transformFormEntryWithSubEntries(formEntryElement);
|
||||
createAndAppendNewSubEntryButton(formEntryElement);
|
||||
}
|
||||
});
|
||||
// // Transform the remaining form entry elements to fix their structure
|
||||
// for (let formEntryElement of formEntryElementsWithSubEntries) {
|
||||
// //transformFormEntryWithSubEntries(formEntryElement);
|
||||
// //createAndAppendNewSubEntryButton(formEntryElement);
|
||||
// }
|
||||
// });
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class SubEntry {
|
||||
constructor(subEntryElement, index, parentFormEntry) {
|
||||
this.parentFormEntry = parentFormEntry;
|
||||
this.index = index;
|
||||
|
||||
this.subEntryElement = subEntryElement;
|
||||
this.removeButtonElement = subEntryElement.querySelector(".remove-button");
|
||||
this.deletionCheckboxElement = subEntryElement.querySelector("input[type=checkbox]");
|
||||
|
||||
this.addPlaceholderAttributes();
|
||||
this.startHandlingSubEntryRemoveButtonClicks();
|
||||
}
|
||||
|
||||
addPlaceholderAttributes() {
|
||||
// Add the type placeholder to the first input element
|
||||
// and the value placeholder to the second one
|
||||
const inputElements = this.subEntryElement.querySelectorAll("input");
|
||||
inputElements[0].setAttribute("placeholder", this.parentFormEntry.placeholders.type);
|
||||
inputElements[1].setAttribute("placeholder", this.parentFormEntry.placeholders.value);
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.subEntryElement.classList.add("hidden");
|
||||
}
|
||||
|
||||
display() {
|
||||
this.subEntryElement.classList.remove("hidden");
|
||||
}
|
||||
|
||||
markForDeletionAndHide() {
|
||||
// Check Django's deletion checkbox
|
||||
this.deletionCheckboxElement.checked = true;
|
||||
|
||||
this.hide();
|
||||
}
|
||||
|
||||
reset() {
|
||||
// Clear all input fields
|
||||
for (let inputElement of this.subEntryElement.querySelectorAll("input")) {
|
||||
inputElement.value = "";
|
||||
}
|
||||
|
||||
// Uncheck Django's deletion checkbox
|
||||
this.deletionCheckboxElement.checked = false;
|
||||
}
|
||||
|
||||
resetAndDisplay() {
|
||||
this.reset();
|
||||
this.display();
|
||||
}
|
||||
|
||||
reindex(newIndex) {
|
||||
// Replace -<old index>- by -<new index>- in the values of the
|
||||
// id and name attributes of every child of this sub-entry's element
|
||||
const attributesToUpdate = ["id", "name"];
|
||||
|
||||
for (let childElement of this.subEntryElement.childNodes) {
|
||||
for (let attribute of attributesToUpdate) {
|
||||
if (!childElement.hasAttribute(attribute)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const newValue = childElement
|
||||
.getAttribute(attribute)
|
||||
.replace(`-${this.index}-`, `-${newIndex}-`);
|
||||
childElement.setAttribute(attribute, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
this.index = newIndex;
|
||||
}
|
||||
|
||||
startHandlingSubEntryRemoveButtonClicks() {
|
||||
this.removeButtonElement.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
this.parentFormEntry.removeSubEntry(this);
|
||||
});
|
||||
}
|
||||
|
||||
static fromTemplate(templateElement, index, parentFormEntry) {
|
||||
// Clone the template, update its class,
|
||||
// and replace __prefix__ by the index of the new sub-entry
|
||||
const clonedTemplateElement = templateElement.cloneNode(true);
|
||||
clonedTemplateElement.innerHTML = clonedTemplateElement.innerHTML
|
||||
.replace(/__prefix__/g, index.toString());
|
||||
clonedTemplateElement.className = "form-sub-entry";
|
||||
|
||||
return new SubEntry(clonedTemplateElement, index, parentFormEntry);
|
||||
}
|
||||
}
|
||||
|
||||
class MultiEntryFormEntry {
|
||||
constructor(formEntryElement) {
|
||||
this.formEntryElement = formEntryElement;
|
||||
this.subEntryTemplateElement = formEntryElement.querySelector(".form-sub-entry-template");
|
||||
this.newSubEntryButtonElement = formEntryElement.querySelector(".add-sub-entry-button");
|
||||
this.totalFormsElement = [...formEntryElement.querySelectorAll("input")]
|
||||
.find(element => element.name && element.name.endsWith("TOTAL_FORMS"));
|
||||
this.maxNumFormsElement = [...formEntryElement.querySelectorAll("input")]
|
||||
.find(element => element.name && element.name.endsWith("MAX_NUM_FORMS"));
|
||||
|
||||
this.placeholders = {
|
||||
type: this.formEntryElement.getAttribute("data-type-placeholder"),
|
||||
value: this.formEntryElement.getAttribute("data-value-placeholder")
|
||||
};
|
||||
|
||||
this.maxNbSubEntries = parseInt(this.maxNumFormsElement.value);
|
||||
this.initialNbSubEntries = 0;
|
||||
this.subEntries = [];
|
||||
this.createInitialSubEntries();
|
||||
|
||||
this.startHandlingNewSubEntryButtonClicks();
|
||||
}
|
||||
|
||||
get nbSubEntries() {
|
||||
return this.subEntries
|
||||
.filter(subEntry =>
|
||||
!subEntry.subEntryElement.classList.contains("hidden")
|
||||
)
|
||||
.length;
|
||||
}
|
||||
|
||||
get hasHiddenSubEntries() {
|
||||
return this.subEntries
|
||||
.findIndex(subEntry =>
|
||||
subEntry.subEntryElement.classList.contains("hidden")
|
||||
) >= 0;
|
||||
}
|
||||
|
||||
createInitialSubEntries() {
|
||||
const subEntryElements = this.formEntryElement.querySelectorAll(".form-sub-entry");
|
||||
this.initialNbSubEntries = subEntryElements.length;
|
||||
|
||||
// This loop assumes sub-entries elements are
|
||||
// ordered with no gap according to indices which start at 0
|
||||
for (let [index, element] of [...subEntryElements].entries()) {
|
||||
this.subEntries.push(
|
||||
new SubEntry(element, index, this)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
createNewSubEntry() {
|
||||
if (this.nbSubEntries === this.maxNbSubEntries) {
|
||||
console.log(`The max. number of sub-entries has been reached (${this.maxNbSubEntries}).`);
|
||||
return;
|
||||
}
|
||||
|
||||
// If there are hidden sub-entries,
|
||||
// it means one of the initial sub-entry elements should be reset and displayed
|
||||
if (this.hasHiddenSubEntries) {
|
||||
// Reset and display the first hidden sub-entry
|
||||
const existingSubEntry = this.subEntries.find(
|
||||
subEntry => subEntry.subEntryElement.classList.contains("hidden")
|
||||
);
|
||||
|
||||
existingSubEntry.resetAndDisplay();
|
||||
}
|
||||
|
||||
// Otherwise, it means a new sub-entry (element) must be created
|
||||
// and appended to the form entry element
|
||||
else {
|
||||
const newSubEntryIndex = this.nbSubEntries;
|
||||
const newSubEntry = SubEntry.fromTemplate(
|
||||
this.subEntryTemplateElement,
|
||||
newSubEntryIndex,
|
||||
this
|
||||
);
|
||||
|
||||
this.subEntries.push(newSubEntry);
|
||||
this.formEntryElement.insertBefore(
|
||||
newSubEntry.subEntryElement,
|
||||
this.newSubEntryButtonElement
|
||||
);
|
||||
|
||||
// Increment Django's TOTAL_FORMS hidden form input
|
||||
this.totalFormsElement.value = (parseInt(this.totalFormsElement.value) + 1).toString();
|
||||
}
|
||||
}
|
||||
|
||||
removeSubEntry(subEntry) {
|
||||
// If the index of the sub-entry to remove is below the initial number of sub-entries,
|
||||
// it means one of the initial sub entry elements should be marked for deletion and hidden
|
||||
const removedSubEntryIndex = subEntry.index;
|
||||
if (removedSubEntryIndex < this.initialNbSubEntries) {
|
||||
subEntry.markForDeletionAndHide();
|
||||
}
|
||||
|
||||
// Otherwise, delete the sub-entry (and remove its element from the DOM)
|
||||
// and reindex other user-created sub-entries if need be
|
||||
else {
|
||||
subEntry.subEntryElement.remove();
|
||||
|
||||
this.subEntries.splice(subEntry.index, 1);
|
||||
for (let index = this.removeSubEntryIndex; index < this.subEntries.length; index++) {
|
||||
this.subEntries[index].reindex(index);
|
||||
}
|
||||
|
||||
// Decrement Django's TOTAL_FORMS hidden form input
|
||||
this.totalFormsElement.value = (parseInt(this.totalFormsElement.value) - 1).toString();
|
||||
}
|
||||
}
|
||||
|
||||
startHandlingNewSubEntryButtonClicks() {
|
||||
const buttonElement = this.formEntryElement.querySelector(".add-sub-entry-button");
|
||||
buttonElement.addEventListener("click", event => {
|
||||
this.createNewSubEntry();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Setup the script by creating one instance of MultiEntryFormEntry
|
||||
// for each form entry configured to contain several sub-entries
|
||||
document.addEventListener("DOMContentLoaded", event => {
|
||||
const multiEntryFormEntryElements = document.querySelectorAll(
|
||||
"#content-edit-profile .form-entry.multi-entry"
|
||||
);
|
||||
|
||||
for (let element of multiEntryFormEntryElements) {
|
||||
const formEntry = new MultiEntryFormEntry(element);
|
||||
console.log("New form entry:", formEntry);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -199,6 +199,10 @@
|
|||
column-gap: 10px;
|
||||
margin: 0 0 10px 0;
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// Since the different labels and inputs are not obvious to identifiate using CSS selectors,
|
||||
// they are selected one after the other using their natural order in the DOM
|
||||
// TODO: make this more robust by giving proper class names to each sub-entry element
|
||||
|
@ -219,6 +223,14 @@
|
|||
.remove-button:active {
|
||||
background-color: #973232;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.form-sub-entry-template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.add-sub-entry-button {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
<div id="content-edit-profile" class="content">
|
||||
<h2>{% trans "Modifier ma page d'annuaire" %}</h2>
|
||||
{{ form.errors }}
|
||||
{{ form.non_field_errors }}
|
||||
<form method="post" action="" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<div class="form-entry">
|
||||
|
@ -63,21 +63,101 @@
|
|||
<label for="id_thurne">{% trans "Thurne :" %}</label>
|
||||
{{ form.thurne }}
|
||||
</div>
|
||||
<div class="form-entry">
|
||||
<div
|
||||
class="form-entry multi-entry"
|
||||
data-type-placeholder="{% trans "Personnel" %}"
|
||||
data-value-placeholder="{% trans "0612345678" %}"
|
||||
>
|
||||
{{ phone_form.non_field_errors }}
|
||||
{{ phone_form.management_form }}
|
||||
<label for="id_phone">{% trans "Numéro(s) de téléphone :" %}</label>
|
||||
{{ phone_form }}
|
||||
{% for form in phone_form %}
|
||||
<div class="form-sub-entry">
|
||||
{{ form.name }}
|
||||
{{ form.number }}
|
||||
{{ form.DELETE }}
|
||||
<button type=button" class="remove-button"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="form-sub-entry-template">
|
||||
{{ phone_form.empty_form.name }}
|
||||
{{ phone_form.empty_form.number }}
|
||||
{{ phone_form.empty_form.DELETE }}
|
||||
<button type="button" class="remove-button"></button>
|
||||
</div>
|
||||
<button type="button" class="add-sub-entry-button">{% trans "Ajouter un numéro" %}</button>
|
||||
</div>
|
||||
<div class="form-entry">
|
||||
<label for="id_social">{% trans "Réseaux sociaux :" %}</label>
|
||||
{{ social_form }}
|
||||
<div
|
||||
class="form-entry multi-entry"
|
||||
data-type-placeholder="{% trans "InstaTok" %}"
|
||||
data-value-placeholder="{% trans "mon_profil_instatok" %}"
|
||||
>
|
||||
{{ social_form.non_field_errors }}
|
||||
{{ social_form.management_form }}
|
||||
<label for="id_social">{% trans "Réseaux sociaux :" %}</label>
|
||||
{% for form in social_form %}
|
||||
<div class="form-sub-entry">
|
||||
{{ form.name }}
|
||||
{{ form.content }}
|
||||
{{ form.DELETE }}
|
||||
<button type=button" class="remove-button"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="form-sub-entry-template">
|
||||
{{ social_form.empty_form.name }}
|
||||
{{ social_form.empty_form.content }}
|
||||
{{ social_form.empty_form.DELETE }}
|
||||
<button type="button" class="remove-button"></button>
|
||||
</div>
|
||||
<button type="button" class="add-sub-entry-button">{% trans "Ajouter un réseau social" %}</button>
|
||||
</div>
|
||||
<div class="form-entry">
|
||||
<label for="id_mail">{% trans "Mail(s):" %}</label>
|
||||
{{ mail_form }}
|
||||
<div
|
||||
class="form-entry multi-entry"
|
||||
data-type-placeholder="{% trans "Professionelle" %}"
|
||||
data-value-placeholder="{% trans "moi@ens.fr" %}"
|
||||
>
|
||||
{{ mail_form.non_field_errors }}
|
||||
{{ mail_form.management_form }}
|
||||
<label for="id_mail">{% trans "Mail(s):" %}</label>
|
||||
{% for form in mail_form %}
|
||||
<div class="form-sub-entry">
|
||||
{{ form.name }}
|
||||
{{ form.mail }}
|
||||
{{ form.DELETE }}
|
||||
<button type=button" class="remove-button"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="form-sub-entry-template">
|
||||
{{ mail_form.empty_form.name }}
|
||||
{{ mail_form.empty_form.mail }}
|
||||
{{ mail_form.empty_form.DELETE }}
|
||||
<button type="button" class="remove-button"></button>
|
||||
</div>
|
||||
<button type="button" class="add-sub-entry-button">{% trans "Ajouter un email" %}</button>
|
||||
</div>
|
||||
<div class="form-entry">
|
||||
<label for="id_address">{% trans "Adresse(s):" %}</label>
|
||||
{{ address_form }}
|
||||
<div
|
||||
class="form-entry multi-entry"
|
||||
data-type-placeholder="{% trans "Bureau" %}"
|
||||
data-value-placeholder="{% trans "45 rue d'Ulm" %}"
|
||||
>
|
||||
{{ address_form.non_field_errors }}
|
||||
{{ address_form.management_form }}
|
||||
<label for="id_address">{% trans "Adresse(s):" %}</label>
|
||||
{% for form in address_form %}
|
||||
<div class="form-sub-entry">
|
||||
{{ form.name }}
|
||||
{{ form.content }}
|
||||
{{ form.DELETE }}
|
||||
<button type=button" class="remove-button"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="form-sub-entry-template">
|
||||
{{ address_form.empty_form.name }}
|
||||
{{ address_form.empty_form.content }}
|
||||
{{ address_form.empty_form.DELETE }}
|
||||
<button type="button" class="remove-button"></button>
|
||||
</div>
|
||||
<button type="button" class="add-sub-entry-button">{% trans "Ajouter une adresse" %}</button>
|
||||
</div>
|
||||
<div class="form-entry">
|
||||
<label for="id_text_field">{% trans "Champ libre :" %}</label>
|
||||
|
|
Loading…
Reference in a new issue