mirror of https://github.com/apache/superset.git
Preserving the ordering in selectmultiple
This commit is contained in:
parent
de52449c2a
commit
c8faeed5b3
|
@ -1,22 +1,40 @@
|
||||||
from wtforms import (
|
from wtforms import (
|
||||||
Field, Form, SelectMultipleField, SelectField, TextField, TextAreaField,
|
Field, Form, SelectMultipleField, SelectField, TextField, TextAreaField,
|
||||||
BooleanField, IntegerField, HiddenField)
|
BooleanField, IntegerField, HiddenField)
|
||||||
from wtforms import validators
|
from wtforms import validators, widgets
|
||||||
from wtforms.widgets import HTMLString
|
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from panoramix import app
|
from panoramix import app
|
||||||
from six import string_types
|
from six import string_types
|
||||||
|
from collections import OrderedDict
|
||||||
config = app.config
|
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):
|
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):
|
def __call__(self, **kwargs):
|
||||||
html = super(BetterBooleanField, self).__call__(**kwargs)
|
html = super(BetterBooleanField, self).__call__(**kwargs)
|
||||||
html += u'<input type="hidden" name="show_brush" value="false">'
|
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):
|
class OmgWtForm(Form):
|
||||||
|
@ -61,7 +79,7 @@ class FormFactory(object):
|
||||||
default='table',
|
default='table',
|
||||||
choices=[(k, v.verbose_name) for k, v in viz_types.items()],
|
choices=[(k, v.verbose_name) for k, v in viz_types.items()],
|
||||||
description="The type of visualization to display"),
|
description="The type of visualization to display"),
|
||||||
'metrics': SelectMultipleField(
|
'metrics': BetterSelectMultipleField(
|
||||||
'Metrics', choices=datasource.metrics_combo,
|
'Metrics', choices=datasource.metrics_combo,
|
||||||
default=[default_metric],
|
default=[default_metric],
|
||||||
description="One or many metrics to display"),
|
description="One or many metrics to display"),
|
||||||
|
@ -69,7 +87,7 @@ class FormFactory(object):
|
||||||
'Metric', choices=datasource.metrics_combo,
|
'Metric', choices=datasource.metrics_combo,
|
||||||
default=default_metric,
|
default=default_metric,
|
||||||
description="One or many metrics to display"),
|
description="One or many metrics to display"),
|
||||||
'groupby': SelectMultipleField(
|
'groupby': BetterSelectMultipleField(
|
||||||
'Group by',
|
'Group by',
|
||||||
choices=self.choicify(datasource.groupby_column_names),
|
choices=self.choicify(datasource.groupby_column_names),
|
||||||
description="One or many fields to group by"),
|
description="One or many fields to group by"),
|
||||||
|
@ -221,7 +239,7 @@ class FormFactory(object):
|
||||||
datasource = viz.datasource
|
datasource = viz.datasource
|
||||||
field_css_classes = {k: ['form-control'] for k in px_form_fields.keys()}
|
field_css_classes = {k: ['form-control'] for k in px_form_fields.keys()}
|
||||||
select2 = [
|
select2 = [
|
||||||
'viz_type', 'metrics', 'groupby',
|
'viz_type', 'groupby',
|
||||||
'row_limit', 'rolling_type', 'series',
|
'row_limit', 'rolling_type', 'series',
|
||||||
'entity', 'x', 'y', 'size', 'rotation', 'metric', 'limit',
|
'entity', 'x', 'y', 'size', 'rotation', 'metric', 'limit',
|
||||||
'markup_type',]
|
'markup_type',]
|
||||||
|
@ -232,6 +250,7 @@ class FormFactory(object):
|
||||||
field_css_classes[field] += ['input-sm']
|
field_css_classes[field] += ['input-sm']
|
||||||
for field in select2:
|
for field in select2:
|
||||||
field_css_classes[field] += ['select2']
|
field_css_classes[field] += ['select2']
|
||||||
|
field_css_classes['metrics'] += ['select2Sortable']
|
||||||
|
|
||||||
|
|
||||||
class QueryForm(OmgWtForm):
|
class QueryForm(OmgWtForm):
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -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));
|
|
@ -29,6 +29,7 @@ function initializeDatasourceView() {
|
||||||
}
|
}
|
||||||
|
|
||||||
$(".select2").select2();
|
$(".select2").select2();
|
||||||
|
$(".select2Sortable").select2Sortable();
|
||||||
$("form").show();
|
$("form").show();
|
||||||
$('[data-toggle="tooltip"]').tooltip({container: 'body'});
|
$('[data-toggle="tooltip"]').tooltip({container: 'body'});
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,12 @@
|
||||||
{{super()}}
|
{{super()}}
|
||||||
<link rel="shortcut icon" href="{{ url_for('static', filename='chaudron.png') }}" />
|
<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/panoramix.css" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="/static/lib/select2.sortable.css" />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block tail_js %}
|
{% block tail_js %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
<script src="/static/panoramix.js"></script>
|
<script src="/static/panoramix.js"></script>
|
||||||
|
<script src="/static/lib/jquery-ui.min.js"></script>
|
||||||
|
<script src="/static/lib/select2.sortable.js"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
Loading…
Reference in New Issue