Skip to content

Commit

Permalink
Preserving the ordering in selectmultiple
Browse files Browse the repository at this point in the history
  • Loading branch information
mistercrunch committed Nov 24, 2015
1 parent de52449 commit c8faeed
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 9 deletions.
37 changes: 28 additions & 9 deletions panoramix/forms.py
Original file line number Diff line number Diff line change
@@ -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):
Expand Down Expand Up @@ -61,15 +79,15 @@ def __init__(self, viz):
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"),
'metric': SelectField(
'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"),
Expand Down Expand Up @@ -221,7 +239,7 @@ def get_form(self, previous=False):
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',]
Expand All @@ -232,6 +250,7 @@ def get_form(self, previous=False):
field_css_classes[field] += ['input-sm']
for field in select2:
field_css_classes[field] += ['select2']
field_css_classes['metrics'] += ['select2Sortable']


class QueryForm(OmgWtForm):
Expand Down
7 changes: 7 additions & 0 deletions panoramix/static/lib/jquery-ui.min.css

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions panoramix/static/lib/jquery-ui.min.js

Large diffs are not rendered by default.

146 changes: 146 additions & 0 deletions panoramix/static/lib/select2.sortable.js
Original file line number Diff line number Diff line change
@@ -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));
1 change: 1 addition & 0 deletions panoramix/static/panoramix.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ function initializeDatasourceView() {
}

$(".select2").select2();
$(".select2Sortable").select2Sortable();
$("form").show();
$('[data-toggle="tooltip"]').tooltip({container: 'body'});

Expand Down
3 changes: 3 additions & 0 deletions panoramix/templates/panoramix/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -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 %}

0 comments on commit c8faeed

Please sign in to comment.