about summary refs log tree commit diff
path: root/bh20simplewebuploader/static
diff options
context:
space:
mode:
authorPjotr Prins2020-05-06 09:06:35 -0500
committerGitHub2020-05-06 09:06:35 -0500
commit6b4c3697ef59324ef5489c46ed9b1f8a101754d1 (patch)
tree12ca85281091879b67741b02d1235151b98167f4 /bh20simplewebuploader/static
parentb6d846b5de6c67b28adab1fa520953115a1a1e30 (diff)
parentfa04ea5388a46746bc219e9bd4adef1d973b9d19 (diff)
downloadbh20-seq-resource-6b4c3697ef59324ef5489c46ed9b1f8a101754d1.tar.gz
bh20-seq-resource-6b4c3697ef59324ef5489c46ed9b1f8a101754d1.tar.lz
bh20-seq-resource-6b4c3697ef59324ef5489c46ed9b1f8a101754d1.zip
Merge pull request #50 from adamnovak/upload-lists
Add support for lists in the web uploader
Diffstat (limited to 'bh20simplewebuploader/static')
-rw-r--r--bh20simplewebuploader/static/main.css18
-rw-r--r--bh20simplewebuploader/static/main.js128
2 files changed, 128 insertions, 18 deletions
diff --git a/bh20simplewebuploader/static/main.css b/bh20simplewebuploader/static/main.css
index 57e29ef..c881253 100644
--- a/bh20simplewebuploader/static/main.css
+++ b/bh20simplewebuploader/static/main.css
@@ -167,16 +167,19 @@ pre code {
     border: solid 1px black;
 }
 
-.record {
+.record, .record .field-group, .record .field-group .field {
     display: flex;
     flex-direction: column;
+    -webkit-column-break-inside: avoid; /* Chrome, Safari, Opera */
+    page-break-inside: avoid; /* Firefox */
+    break-inside: avoid;
+}
+
+.record {
     border: solid 1px #808080;
     padding: 1em;
     background: #F8F8F8;
     margin-bottom: 1em;
-    -webkit-column-break-inside: avoid; /* Chrome, Safari, Opera */
-    page-break-inside: avoid; /* Firefox */
-    break-inside: avoid;
 }
 
 .record label {
@@ -232,13 +235,6 @@ footer {
 .sponsors img {
     width: 100%;
 }
-.metadata input#metadata_upload:checked ~ #metadata_upload_form_spot {
-    display: block;
-}
-
-.metadata input#metadata_upload ~ #metadata_upload_form_spot {
-    display: none;
-}
 
 .loader {
     display: block;
diff --git a/bh20simplewebuploader/static/main.js b/bh20simplewebuploader/static/main.js
index 96199a0..56213fa 100644
--- a/bh20simplewebuploader/static/main.js
+++ b/bh20simplewebuploader/static/main.js
@@ -34,14 +34,128 @@ let fetchAllaccessions = () => {
   fetchAPI("/api/getAllaccessions");
 };
 
+/*
+ * Make sure that only one of the manual metadata entry and metadata upload
+ * form components is *actually* a child of the form element in the DOM.
+ *
+ * Because both make use of the "required" attribute, we can't get away with
+ * just hiding the one we don't want the user to fill in. The hidden part will
+ * still have possibly empty required fields and (some) browsers will
+ * blocksubmission because of it. Moreover, the data (including file uploads)
+ * from the hidden elements will still be sent to the server, which the user
+ * may not expect.
+ */
+
+let uploadForm = document.getElementById('metadata_upload_form')
+let uploadFormSpot = document.getElementById('metadata_upload_form_spot')
+let fillForm = document.getElementById('metadata_fill_form')
+let fillFormSpot = document.getElementById('metadata_fill_form_spot') 
+
+function setUploadMode() {
+  // Make the upload form the one in use.
+  uploadFormSpot.appendChild(uploadForm)
+  // Remove the upload form from the DOM so its required-ness does not block submission.
+  fillFormSpot.removeChild(fillForm)
+}
+
+function setFillMode() {
+  // Make the fillable form the one in use
+  uploadFormSpot.removeChild(uploadForm)
+  // Remove the fillable form from the DOM so its required-ness does not block submission.
+  fillFormSpot.appendChild(fillForm)
+}
+
+function setMode() {
+  // Pick mode based on radio
+  if (document.getElementById('metadata_upload').checked) {
+    setUploadMode()
+ } else {
+    setFillMode()
+ }
+}
+
+/*
+ * Machinery for variable-length lists of input items.
+ */
+
+// Start in mode appropriate to selected form item.
+// It is important that we run this code when the page starts! The browser may
+// have set the radio button to whatever the state was on last page load,
+// instead of the default state, without raising an event, and we have to
+// handle that.
+setMode()
+
+/**
+ * Add another form field to the group this button is part of.
+ */
+function addField(e) {
+  // Find our parent field-group div
+  let fieldGroup = this.parentElement
+  
+  // Get its keypath
+  let keypath = fieldGroup.dataset.keypath
+  
+  // Find its last field child
+  let existingFields = fieldGroup.getElementsByClassName('field')
+  let templateField = existingFields[existingFields.length - 1]
+  
+  // Get its number
+  let fieldNumber = templateField.dataset.number
+  
+  // Duplicate it
+  let newField = templateField.cloneNode(true)
+  
+  // Increment the number and use the keypath and number to set IDs and cross
+  // references.
+  // TODO: Heavily dependent on the form field HTML. Maybe we want custom
+  // elements for the labeled controlsd that know how to be list items?
+  fieldNumber++
+  newField.dataset.number = fieldNumber
+  let newID = keypath + '[' + fieldNumber + ']'
+  let newControl = newField.getElementsByClassName('control')[0]
+  newControl.id = newID
+  newControl.setAttribute('name', newID)
+  let newLabel = newField.getElementsByTagName('label')[0]
+  newLabel.setAttribute('for', newID)
+  
+  // Find the minus button
+  let minusButton = fieldGroup.getElementsByClassName('remove-field')[0]
+  
+  // Put new field as a child before the minus button
+  fieldGroup.insertBefore(newField, minusButton)
+  
+  // Enable the minus button
+  minusButton.classList.remove('invisible')
+}
+
 /**
- * Show form if checked
+ * Remove the last form field from the group button is part of.
  */
-let fillFormSpot = document.getElementById('metadata_fill_form_spot');
-function displayForm() {
-  if (document.getElementById('metadata_form').checked) {
-    fillFormSpot.classList.remove("invisible");
-    return;
+function removeField(e) {
+  // Find our parent field-group div
+  let fieldGroup = this.parentElement
+  
+  // Find its field children
+  let existingFields = fieldGroup.getElementsByClassName('field')
+  
+  if (existingFields.length > 1) {
+    // There is a last field we can safely remove.
+    let lastField = existingFields[existingFields.length - 1]
+    fieldGroup.removeChild(lastField)
   }
-  fillFormSpot.classList.add("invisible");
+  
+  if (existingFields.length <= 1) {
+    // Collection auto-updates. Now there's only one element. Don't let the
+    // user remove it. If they don't want it, they can leave it empty.
+    this.classList.add('invisible')
+  }
+}
+
+// Find all the add and remove field buttons and hook up the listeners.
+for (let button of document.getElementsByClassName('add-field')) {
+  button.addEventListener('click', addField)
 }
+for (let button of document.getElementsByClassName('remove-field')) {
+  button.addEventListener('click', removeField)
+}
+