diff options
author | Adam Novak | 2020-05-05 14:27:58 -0700 |
---|---|---|
committer | Adam Novak | 2020-05-05 14:27:58 -0700 |
commit | 07ff2d0f44d07bcca830f020e72ae2389a909f4f (patch) | |
tree | 3303cf2a6785164bc30253d49ac57098b47028e5 | |
parent | 0add6e53959fd0e7395f35289d958827b8d5a611 (diff) | |
download | bh20-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.py | 3 | ||||
-rw-r--r-- | bh20simplewebuploader/static/main.css | 15 | ||||
-rw-r--r-- | bh20simplewebuploader/static/main.js | 75 | ||||
-rw-r--r-- | bh20simplewebuploader/templates/form.html | 56 |
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> |