aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Novak2020-05-05 14:27:58 -0700
committerAdam Novak2020-05-05 14:27:58 -0700
commit07ff2d0f44d07bcca830f020e72ae2389a909f4f (patch)
tree3303cf2a6785164bc30253d49ac57098b47028e5
parent0add6e53959fd0e7395f35289d958827b8d5a611 (diff)
downloadbh20-seq-resource-07ff2d0f44d07bcca830f020e72ae2389a909f4f.tar.gz
bh20-seq-resource-07ff2d0f44d07bcca830f020e72ae2389a909f4f.tar.lz
bh20-seq-resource-07ff2d0f44d07bcca830f020e72ae2389a909f4f.zip
Add JS at front end for lists, and date support on backend
-rw-r--r--bh20simplewebuploader/main.py3
-rw-r--r--bh20simplewebuploader/static/main.css15
-rw-r--r--bh20simplewebuploader/static/main.js75
-rw-r--r--bh20simplewebuploader/templates/form.html56
4 files changed, 121 insertions, 28 deletions
diff --git a/bh20simplewebuploader/main.py b/bh20simplewebuploader/main.py
index 7b6e6e1..1a441f0 100644
--- a/bh20simplewebuploader/main.py
+++ b/bh20simplewebuploader/main.py
@@ -268,6 +268,9 @@ def parse_input(input_string, html_type, number_step=None):
return int(input_string)
else:
return float(input_string)
+ elif html_type == 'date':
+ # Don't do our own date validation; pass it on as a string
+ return input_string
else:
raise NotImplementedError('Unimplemented input type: {}'.format(html_type))
diff --git a/bh20simplewebuploader/static/main.css b/bh20simplewebuploader/static/main.css
index 57e29ef..80ee6b7 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 {
@@ -184,6 +187,10 @@ pre code {
margin-top: 10px;
}
+.hidden {
+ display: none;
+}
+
.search-section {
display: flex;
justify-content: space-between;
diff --git a/bh20simplewebuploader/static/main.js b/bh20simplewebuploader/static/main.js
index 96199a0..a67d3df 100644
--- a/bh20simplewebuploader/static/main.js
+++ b/bh20simplewebuploader/static/main.js
@@ -45,3 +45,78 @@ function displayForm() {
}
fillFormSpot.classList.add("invisible");
}
+
+/**
+ * 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('hidden')
+}
+
+/**
+ * Remove the last form field from the group button is part of.
+ */
+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)
+ }
+
+ 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('hidden')
+ }
+}
+
+// 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)
+}
+
diff --git a/bh20simplewebuploader/templates/form.html b/bh20simplewebuploader/templates/form.html
index ffd4158..cea444c 100644
--- a/bh20simplewebuploader/templates/form.html
+++ b/bh20simplewebuploader/templates/form.html
@@ -32,10 +32,10 @@
<div class="search">
<input id="search-input" id="global-search" type="search" placeholder="FASTA uri" required>
<button class="button search-button" type="submit" onclick="search()">
- <span class="icon ion-search">
- <span class="sr-only">Search</span>
- </span>
- </button>
+ <span class="icon ion-search">
+ <span class="sr-only">Search</span>
+ </span>
+ </button>
</div>
</section>
@@ -116,26 +116,34 @@
<div class="record">
<h4>{{ record['heading'] }}</h4>
{% else %}
- <label for="{{ record['id'] }}" title="{{ record.get('docstring', '') }}">
- {{ record['label'] }}
- {{ "*" if record['required'] else "" }}
- {% if 'docstring' in record %}
- <a href='javascript:alert({{ record['docstring'] | tojson }})'>❓</a>
+ <div class="field-group" data-keypath="{{ record['id'] }}">
+ <div class="field" data-number="0">
+ <label for="{{ record['id'] }}{{ '[0]' if record['list'] else ''}}" title="{{ record.get('docstring', '') }}">
+ {{ record['label'] }}
+ {{ "*" if record['required'] else "" }}
+ {% if 'docstring' in record %}
+ <a href='javascript:alert({{ record['docstring'] | tojson }})'>❓</a>
+ {% endif %}
+ {% if 'ref_iri' in record %}
+ <a href="{{ record['ref_iri'] }}" target="_blank" title="Ontology Link">🔗</a>
+ {% endif %}
+ </label>
+ {% if record['type'] == 'select' %}
+ <select class="control" id="{{ record['id'] }}{{ '[0]' if record['list'] else ''}}" name="{{ record['id'] }}{{ '[0]' if record['list'] else ''}}" {{ "required" if record['required'] else "" }}>
+ <option value="" selected>Choose one...</option>
+ {% for option in record['options'] %}
+ <option value="{{ option[1] }}">{{ option[0] }}</option>
+ {% endfor %}
+ </select>
+ {% else %}
+ <input class="control" type="{{ record['type'] }}" id="{{ record['id'] }}{{ '[0]' if record['list'] else ''}}" name="{{ record['id'] }}{{ '[0]' if record['list'] else ''}}" {{ "required" if record['required'] else "" }} {{ ("step=" + record['step']) if 'step' in record else ""}}>
+ {% endif %}
+ </div>
+ {% if record['list'] %}
+ <button type="button" title="Remove field" class="remove-field hidden">➖</button>
+ <button type="button" title="Add field" class="add-field">➕</button>
{% endif %}
- {% if 'ref_iri' in record %}
- <a href="{{ record['ref_iri'] }}" target="_blank" title="Ontology Link">🔗</a>
- {% endif %}
- </label>
- {% if record['type'] == 'select' %}
- <select id="{{ record['id'] }}" name="{{ record['id'] }}" {{ "required" if record['required'] else "" }}>
- <option value="" selected>Choose one...</option>
- {% for option in record['options'] %}
- <option value="{{ option[1] }}">{{ option[0] }}</option>
- {% endfor %}
- </select>
- {% else %}
- <input type="{{ record['type'] }}" id="{{ record['id'] }}" name="{{ record['id'] }}" {{ "required" if record['required'] else "" }} {{ ("step=" + record['step']) if 'step' in record else ""}}>
- {% endif %}
+ </div>
{% endif %}
{% if loop.index == loop.length %}
</div>
@@ -190,7 +198,7 @@
</div>
<script type="text/javascript">
- let scriptRoot = {{ request.script_root|tojson|safe }};
+ let scriptRoot = {{ request.script_root|tojson|safe }};
</script>
<script type="text/javascript" src="/static/main.js"></script>