Add fancy multichoice selector

Fixes #47
This commit is contained in:
rubenwardy 2018-05-25 16:53:25 +01:00
parent ebd99165e9
commit 211ed7c6fd
No known key found for this signature in database
GPG Key ID: A1E29D52FF81513C
20 changed files with 250 additions and 46 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

7
app/public/static/jquery-ui.min.css vendored Normal file

File diff suppressed because one or more lines are too long

9
app/public/static/jquery-ui.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,99 @@
/*!
* Tag Selector plugin for jQuery: Facilitates selecting multiple tags by extending jQuery UI Autocomplete.
* You may use Tag Selector under the terms of either the MIT License or the GNU General Public License (GPL) Version 2.
* https://petprojects.googlecode.com/svn/trunk/MIT-LICENSE.txt
* https://petprojects.googlecode.com/svn/trunk/GPL-LICENSE.txt
*/
(function($) {
$.fn.tagSelector = function(source, name, select) {
return this.each(function() {
var selector = $(this),
input = $('input[type=text]', this);
selector.click(function() { input.focus(); })
.delegate('.tag a', 'click', function() {
var id = $(this).parent().data("id");
for (var i = 0; i < source.length; i++) {
if (source[i].id == id) {
source[i].selected = null;
}
}
select.find("option[value=" + id + "]").attr("selected", null)
recreate();
});
function addTag(item) {
var tag = $('<span class="tag"/>')
.text(item.toString() + ' ')
.data("id", item.id)
.append('<a>x</a>')
.insertBefore(input);
input.attr("placeholder", null);
select.find("option[value=" + item.id + "]").attr("selected", "selected")
}
function recreate() {
selector.find("span").remove();
for (var i = 0; i < source.length; i++) {
if (source[i].selected) {
addTag(source[i]);
}
}
}
recreate();
input.keydown(function(e) {
if (e.keyCode === $.ui.keyCode.TAB && $(this).data('ui-autocomplete').menu.active)
e.preventDefault();
})
.autocomplete({
minLength: 0,
source: source,
select: function(event, ui) {
addTag(ui.item);
input.val("");
return false;
}
});
input.data('ui-autocomplete')._renderItem = function(ul, item) {
return $('<li/>')
.data('item.autocomplete', item)
.append($('<a/>').text(item.toString()))
.appendTo(ul);
};
input.data('ui-autocomplete')._resizeMenu = function(ul, item) {
var ul = this.menu.element;
ul.outerWidth(Math.max(
ul.width('').outerWidth(),
selector.outerWidth()
));
};
});
}
$(function() {
$(".multichoice_selector").each(function() {
var ele = $(this);
var sel = ele.parent().find("select");
console.log(sel.attr("name"));
sel.css("display", "none");
var options = [];
sel.find("option").each(function() {
var text = $(this).text();
options.push({
id: $(this).attr("value"),
value: text,
selected: $(this).attr("selected") ? true : false,
toString: function() { return text; },
});
});
console.log(options);
ele.tagSelector(options, sel.attr("name"), sel);
})
});
})(jQuery);

View File

@ -87,7 +87,7 @@ a:hover {
}
.button, .buttonset li a, input[type=submit], input[type=text],
input[type=password], textarea, select {
input[type=password], textarea, select, .multichoice_selector {
text-align: center;
display: inline-block;
padding: 0.4em 1em;
@ -101,16 +101,39 @@ a:hover {
header {
.button, .buttonset li a, input[type=submit], input[type=text],
input[type=password], textarea, select {
input[type=password], textarea, select, .multichoice_selector {
background: rgba(255,255,255,0.1);
border: 1px solid rgba(255,255,255,0.2);
}
}
input[type=text], input[type=password], textarea, select {
input[type=text], input[type=password], textarea, select, .multichoice_selector {
text-align: left;
}
.ui-autocomplete, ui-front {
position:absolute;
cursor:default;
z-index:1001 !important
}
.ui-autocomplete {
background: #333 !important;
border: 1px solid #444 !important;
li {
display: block !important;
margin: 0 !important;
padding: 0 !important;
}
a {
display: block;
padding: 0.3em 0.5em !important;
margin: 0 !important;
}
}
select {
min-width: 200px;
}
@ -129,13 +152,13 @@ select:not([multiple]) {
padding: 0 8px 8px 0;
}
.form-group input, .form-group textarea {
.form-group input, .form-group textarea, .form-group .multichoice_selector {
display: block;
min-width: 100%;
max-width: 100%;
}
.box .form-group input, .box .form-group textarea {
.box .form-group input, .box .form-group textarea, .form-group .multichoice_selector {
min-width: 95%;
max-width: 95%;
}
@ -178,6 +201,43 @@ select:not([multiple]) {
margin: 10px 10px 0 0;
}
.multichoice_selector input {
border: none;
border-radius: 0;
-moz-border-radius: 0;
box-shadow: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
width: auto;
min-width: 50px;
float: left;
padding: 4px 0;
white-space: nowrap;
background: transparent;
}
.multichoice_selector .tag {
background: #375D81;
border-radius: 3px;
-moz-border-radius: 3px;
color: #FFF;
float: left;
height: 15px;
padding: 0.1em 0.4em 0.5em;
margin-right: 0.3em;
margin-bottom: 0.3em;
vertical-align: baseline;
}
.multichoice_selector .tag a {
color: #FFF;
cursor: pointer;
}
.multichoice_selector .tag a:hover {
color: #0099CC;
text-decoration: none;
}
/* Alerts */
.alert {

View File

@ -1,42 +1,68 @@
{% macro render_field(field, label=None, label_visible=true, right_url=None, right_label=None) -%}
<div class="form-group {% if field.errors %}has-error{% endif %} {{ kwargs.pop('class_', '') }}">
{% if field.type != 'HiddenField' and label_visible %}
{% if not label %}{% set label=field.label.text %}{% endif %}
<label for="{{ field.id }}" class="control-label">{{ label|safe }}</label>
{% endif %}
{{ field(class_='form-control', **kwargs) }}
{% if field.errors %}
{% for e in field.errors %}
<p class="help-block">{{ e }}</p>
{% endfor %}
{% endif %}
</div>
<div class="form-group {% if field.errors %}has-error{% endif %} {{ kwargs.pop('class_', '') }}">
{% if field.type != 'HiddenField' and label_visible %}
{% if not label %}{% set label=field.label.text %}{% endif %}
<label for="{{ field.id }}" class="control-label">{{ label|safe }}</label>
{% endif %}
{{ field(class_='form-control', **kwargs) }}
{% if field.errors %}
{% for e in field.errors %}
<p class="help-block">{{ e }}</p>
{% endfor %}
{% endif %}
</div>
{%- endmacro %}
{% macro form_includes() -%}
<link href="/static/jquery-ui.min.css" rel="stylesheet" type="text/css">
<script src="/static/jquery.min.js"></script>
<script src="/static/jquery-ui.min.js"></script>
<script src="/static/tagselector.js"></script>
{% endmacro %}
{% macro render_multiselect_field(field, label=None, label_visible=true, right_url=None, right_label=None) -%}
<div class="form-group {% if field.errors %}has-error{% endif %} {{ kwargs.pop('class_', '') }}">
{% if field.type != 'HiddenField' and label_visible %}
{% if not label %}{% set label=field.label.text %}{% endif %}
<label for="{{ field.id }}" class="control-label">{{ label|safe }}</label>
{% endif %}
<div class="multichoice_selector">
<input type="text" placeholder="Start typing to see suggestions">
<div class="clearboth"></div>
</div>
{{ field(class_='form-control', **kwargs) }}
{% if field.errors %}
{% for e in field.errors %}
<p class="help-block">{{ e }}</p>
{% endfor %}
{% endif %}
</div>
{% endmacro %}
{% macro render_checkbox_field(field, label=None) -%}
{% if not label %}{% set label=field.label.text %}{% endif %}
<div class="checkbox">
<label>
{{ field(type='checkbox', **kwargs) }} {{ label }}
</label>
</div>
{% if not label %}{% set label=field.label.text %}{% endif %}
<div class="checkbox">
<label>
{{ field(type='checkbox', **kwargs) }} {{ label }}
</label>
</div>
{%- endmacro %}
{% macro render_radio_field(field) -%}
{% for value, label, checked in field.iter_choices() %}
<div class="radio">
<label>
<input type="radio" name="{{ field.id }}" id="{{ field.id }}" value="{{ value }}"{% if checked %} checked{% endif %}>
{{ label }}
</label>
</div>
{% endfor %}
{% for value, label, checked in field.iter_choices() %}
<div class="radio">
<label>
<input type="radio" name="{{ field.id }}" id="{{ field.id }}" value="{{ value }}"{% if checked %} checked{% endif %}>
{{ label }}
</label>
</div>
{% endfor %}
{%- endmacro %}
{% macro render_submit_field(field, label=None, tabindex=None) -%}
{% if not label %}{% set label=field.label.text %}{% endif %}
{#<button type="submit" class="form-control btn btn-default btn-primary">{{label}}</button>#}
<input type="submit" value="{{label}}"
{% if tabindex %}tabindex="{{ tabindex }}"{% endif %}
>
{% if not label %}{% set label=field.label.text %}{% endif %}
{#<button type="submit" class="form-control btn btn-default btn-primary">{{label}}</button>#}
<input type="submit" value="{{label}}"
{% if tabindex %}tabindex="{{ tabindex }}"{% endif %}
>
{%- endmacro %}

View File

@ -10,7 +10,9 @@
{% block content %}
<h2>Create Package</h2>
{% from "macros/forms.html" import render_field, render_submit_field %}
{% from "macros/forms.html" import render_field, render_submit_field, form_includes, render_multiselect_field %}
{{ form_includes() }}
<form method="POST" action="" class="tableform">
{{ form.hidden_tag() }}
@ -20,9 +22,9 @@
{{ render_field(form.desc, class_="pkg_meta") }}
{{ render_field(form.type, class_="pkg_meta") }}
{{ render_field(form.license, class_="pkg_meta") }}
{{ render_field(form.tags, class_="pkg_meta") }}
{{ render_field(form.harddeps, class_="pkg_meta") }}
{{ render_field(form.softdeps, class_="pkg_meta") }}
{{ render_multiselect_field(form.tags, class_="pkg_meta") }}
{{ render_multiselect_field(form.harddeps, class_="pkg_meta") }}
{{ render_multiselect_field(form.softdeps, class_="pkg_meta") }}
<div class="pkg_wiz_1">
<p>Enter the repo URL for the package.
@ -48,7 +50,6 @@
</form>
{% if enable_wizard %}
<script src="/static/jquery.min.js"></script>
<script src="/static/url.min.js"></script>
<script src="/static/polltask.js"></script>
<script src="/static/package_create.js"></script>

View File

@ -5,7 +5,9 @@
{% endblock %}
{% block content %}
{% from "macros/forms.html" import render_field, render_submit_field %}
{% from "macros/forms.html" import render_field, render_submit_field, form_includes, render_multiselect_field %}
{{ form_includes() }}
<form method="POST" action="">
{{ form.hidden_tag() }}
@ -15,7 +17,7 @@
{{ render_field(form.desc) }}
{{ render_field(form.type) }}
{{ render_field(form.license) }}
{{ render_field(form.tags) }}
{{ render_multiselect_field(form.tags) }}
{{ render_field(form.repo) }}
{{ render_field(form.website) }}
{{ render_field(form.issueTracker) }}

View File

@ -124,9 +124,9 @@ class PackageForm(FlaskForm):
desc = TextAreaField("Long Description", [Optional(), Length(0,10000)])
type = SelectField("Type", [InputRequired()], choices=PackageType.choices(), coerce=PackageType.coerce, default=PackageType.MOD)
license = QuerySelectField("License", [InputRequired()], query_factory=lambda: License.query, get_pk=lambda a: a.id, get_label=lambda a: a.name)
tags = QuerySelectMultipleField('Tags', query_factory=lambda: Tag.query, get_pk=lambda a: a.id, get_label=lambda a: a.title)
harddeps = QuerySelectMultipleField('Dependencies', query_factory=lambda: Package.query, get_pk=lambda a: a.id, get_label=lambda a: a.title + " by " + a.author.display_name)
softdeps = QuerySelectMultipleField('Soft Dependencies', query_factory=lambda: Package.query, get_pk=lambda a: a.id, get_label=lambda a: a.title + " by " + a.author.display_name)
tags = QuerySelectMultipleField('Tags', query_factory=lambda: Tag.query.order_by(db.asc(Tag.name)), get_pk=lambda a: a.id, get_label=lambda a: a.title)
harddeps = QuerySelectMultipleField('Dependencies', query_factory=lambda: Package.query.join(User).order_by(db.asc(Package.title), db.asc(User.display_name)), get_pk=lambda a: a.id, get_label=lambda a: a.title + " by " + a.author.display_name)
softdeps = QuerySelectMultipleField('Soft Dependencies', query_factory=lambda: Package.query.join(User).order_by(db.asc(Package.title), db.asc(User.display_name)), get_pk=lambda a: a.id, get_label=lambda a: a.title + " by " + a.author.display_name)
repo = StringField("Repo URL", [Optional(), URL()])
website = StringField("Website URL", [Optional(), URL()])
issueTracker = StringField("Issue Tracker URL", [Optional(), URL()])