Preserving the ordering in selectmultiple

This commit is contained in:
Maxime Beauchemin 2015-11-24 10:52:32 -08:00
parent de52449c2a
commit c8faeed5b3
6 changed files with 198 additions and 9 deletions

View File

@ -1,22 +1,40 @@
from wtforms import (
Field, Form, SelectMultipleField, SelectField, TextField, TextAreaField,
BooleanField, IntegerField, HiddenField)
from wtforms import validators
from wtforms.widgets import HTMLString
from wtforms import validators, widgets
from copy import copy
from panoramix import app
from six import string_types
from collections import OrderedDict
config = app.config
# Fixes behavior of html forms omitting non checked <input>
# (which doesn't distinguish False from NULL/missing )
# If value is unchecked, this hidden <input> fills in False value
class BetterBooleanField(BooleanField):
"""
Fixes behavior of html forms omitting non checked <input>
(which doesn't distinguish False from NULL/missing )
If value is unchecked, this hidden <input> fills in False value
"""
def __call__(self, **kwargs):
html = super(BetterBooleanField, self).__call__(**kwargs)
html += u'<input type="hidden" name="show_brush" value="false">'
return HTMLString(html)
return widgets.HTMLString(html)
class BetterSelectMultipleField(SelectMultipleField):
"""
Works along with select2sortable to preserves the sort order
"""
def iter_choices(self):
d = OrderedDict()
for value, label in self.choices:
selected = self.data is not None and self.coerce(value) in self.data
d[value] = (value, label, selected)
if self.data:
for value in self.data:
yield d.pop(value)
while d:
yield d.pop(d.keys()[0])
class OmgWtForm(Form):
@ -61,7 +79,7 @@ class FormFactory(object):
default='table',
choices=[(k, v.verbose_name) for k, v in viz_types.items()],
description="The type of visualization to display"),
'metrics': SelectMultipleField(
'metrics': BetterSelectMultipleField(
'Metrics', choices=datasource.metrics_combo,
default=[default_metric],
description="One or many metrics to display"),
@ -69,7 +87,7 @@ class FormFactory(object):
'Metric', choices=datasource.metrics_combo,
default=default_metric,
description="One or many metrics to display"),
'groupby': SelectMultipleField(
'groupby': BetterSelectMultipleField(
'Group by',
choices=self.choicify(datasource.groupby_column_names),
description="One or many fields to group by"),
@ -221,7 +239,7 @@ class FormFactory(object):
datasource = viz.datasource
field_css_classes = {k: ['form-control'] for k in px_form_fields.keys()}
select2 = [
'viz_type', 'metrics', 'groupby',
'viz_type', 'groupby',
'row_limit', 'rolling_type', 'series',
'entity', 'x', 'y', 'size', 'rotation', 'metric', 'limit',
'markup_type',]
@ -232,6 +250,7 @@ class FormFactory(object):
field_css_classes[field] += ['input-sm']
for field in select2:
field_css_classes[field] += ['select2']
field_css_classes['metrics'] += ['select2Sortable']
class QueryForm(OmgWtForm):

7
panoramix/static/lib/jquery-ui.min.css vendored Executable file

File diff suppressed because one or more lines are too long

13
panoramix/static/lib/jquery-ui.min.js vendored Executable file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,146 @@
/**
* jQuery Select2 Sortable
* - enable select2 to be sortable via normal select element
*
* author : Vafour
* modified : Kevin Provance (kprovance)
* inspired by : jQuery Chosen Sortable (https://github.com/mrhenry/jquery-chosen-sortable)
* License : GPL
*/
(function ($) {
$.fn.extend({
select2SortableOrder: function () {
var $this = this.filter('[multiple]');
$this.each(function () {
var $select = $(this);
// skip elements not select2-ed
if (typeof ($select.data('select2')) !== 'object') {
return false;
}
var $select2 = $select.siblings('.select2-container');
var sorted;
// Opt group names
var optArr = [];
$select.find('optgroup').each(function(idx, val) {
optArr.push (val);
});
$select.find('option').each(function(idx, val) {
var groupName = $(this).parent('optgroup').prop('label');
var optVal = this;
if (groupName === undefined) {
if (this.value !== '' && !this.selected) {
optArr.push (optVal);
}
}
});
sorted = $($select2.find('.select2-choices li[class!="select2-search-field"]').map(function () {
if (!this) {
return undefined;
}
var id = $(this).data('select2Data').id;
return $select.find('option[value="' + id + '"]')[0];
}));
sorted.push.apply(sorted, optArr);
$select.children().remove();
$select.append(sorted);
});
return $this;
},
select2Sortable: function () {
var args = Array.prototype.slice.call(arguments, 0);
$this = this.filter('[multiple]'),
validMethods = ['destroy'];
if (args.length === 0 || typeof (args[0]) === 'object') {
var defaultOptions = {
bindOrder: 'formSubmit', // or sortableStop
sortableOptions: {
placeholder: 'ui-state-highlight',
items: 'li:not(.select2-search-field)',
tolerance: 'pointer'
}
};
var options = $.extend(defaultOptions, args[0]);
// Init select2 only if not already initialized to prevent select2 configuration loss
if (typeof ($this.data('select2')) !== 'object') {
$this.select2();
}
$this.each(function () {
var $select = $(this)
var $select2choices = $select.siblings('.select2-container').find('.select2-choices');
// Init jQuery UI Sortable
$select2choices.sortable(options.sortableOptions);
switch (options.bindOrder) {
case 'sortableStop':
// apply options ordering in sortstop event
$select2choices.on("sortstop.select2sortable", function (event, ui) {
$select.select2SortableOrder();
});
$select.on('change', function (e) {
$(this).select2SortableOrder();
});
break;
default:
// apply options ordering in form submit
$select.closest('form').unbind('submit.select2sortable').on('submit.select2sortable', function () {
$select.select2SortableOrder();
});
break;
}
});
}
else if (typeof (args[0] === 'string')) {
if ($.inArray(args[0], validMethods) == -1) {
throw "Unknown method: " + args[0];
}
if (args[0] === 'destroy') {
$this.select2SortableDestroy();
}
}
return $this;
},
select2SortableDestroy: function () {
var $this = this.filter('[multiple]');
$this.each(function () {
var $select = $(this)
var $select2choices = $select.parent().find('.select2-choices');
// unbind form submit event
$select.closest('form').unbind('submit.select2sortable');
// unbind sortstop event
$select2choices.unbind("sortstop.select2sortable");
// destroy select2Sortable
$select2choices.sortable('destroy');
});
return $this;
}
});
}(jQuery));

View File

@ -29,6 +29,7 @@ function initializeDatasourceView() {
}
$(".select2").select2();
$(".select2Sortable").select2Sortable();
$("form").show();
$('[data-toggle="tooltip"]').tooltip({container: 'body'});

View File

@ -4,9 +4,12 @@
{{super()}}
<link rel="shortcut icon" href="{{ url_for('static', filename='chaudron.png') }}" />
<link rel="stylesheet" type="text/css" href="/static/panoramix.css" />
<link rel="stylesheet" type="text/css" href="/static/lib/select2.sortable.css" />
{% endblock %}
{% block tail_js %}
{{ super() }}
<script src="/static/panoramix.js"></script>
<script src="/static/lib/jquery-ui.min.js"></script>
<script src="/static/lib/select2.sortable.js"></script>
{% endblock %}