Add "valueless verbs" concept to grid filters.
Plus some other improvements I'm sure...
This commit is contained in:
parent
3d7cb2d9a7
commit
032d538062
|
@ -93,6 +93,8 @@ class AlchemyGrid(Grid):
|
||||||
factory = filters.AlchemyNumericFilter
|
factory = filters.AlchemyNumericFilter
|
||||||
elif isinstance(column.type, sa.Boolean):
|
elif isinstance(column.type, sa.Boolean):
|
||||||
factory = filters.AlchemyBooleanFilter
|
factory = filters.AlchemyBooleanFilter
|
||||||
|
elif isinstance(column.type, (sa.Date, sa.DateTime)):
|
||||||
|
factory = filters.AlchemyDateFilter
|
||||||
return factory(key, column=column, **kwargs)
|
return factory(key, column=column, **kwargs)
|
||||||
|
|
||||||
def iter_filters(self):
|
def iter_filters(self):
|
||||||
|
|
|
@ -38,29 +38,41 @@ from pyramid_simpleform.renderers import FormRenderer
|
||||||
from webhelpers.html import HTML, tags
|
from webhelpers.html import HTML, tags
|
||||||
|
|
||||||
|
|
||||||
class FilterRenderer(object):
|
class FilterValueRenderer(object):
|
||||||
"""
|
"""
|
||||||
Base class for all filter renderers.
|
Base class for all filter renderers.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def __init__(self, filter=None):
|
||||||
|
self.filter = filter
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self.filter.key
|
||||||
|
|
||||||
def render(self, value=None, **kwargs):
|
def render(self, value=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Render the filter input element(s) as HTML. Default implementation
|
Render the filter input element(s) as HTML. Default implementation
|
||||||
uses a simple text input.
|
uses a simple text input.
|
||||||
"""
|
"""
|
||||||
name = self.filter.key
|
return tags.text(self.name, value=value, **kwargs)
|
||||||
return tags.text(name, value=value, id='filter.{0}.value'.format(name))
|
|
||||||
|
|
||||||
|
|
||||||
class DefaultRenderer(FilterRenderer):
|
class DefaultValueRenderer(FilterValueRenderer):
|
||||||
"""
|
"""
|
||||||
Default / fallback renderer.
|
Default / fallback renderer.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class NumericRenderer(FilterRenderer):
|
class NumericValueRenderer(FilterValueRenderer):
|
||||||
"""
|
"""
|
||||||
Input renderer for numeric fields.
|
Input renderer for numeric values.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class DateValueRenderer(FilterValueRenderer):
|
||||||
|
"""
|
||||||
|
Input renderer for date values.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@ -69,7 +81,7 @@ class GridFilter(object):
|
||||||
Represents a filter available to a grid. This is used to construct the
|
Represents a filter available to a grid. This is used to construct the
|
||||||
'filters' section when rendering the index page template.
|
'filters' section when rendering the index page template.
|
||||||
"""
|
"""
|
||||||
verbmap = {
|
verb_labels = {
|
||||||
'is_any': "is any",
|
'is_any': "is any",
|
||||||
'equal': "equal to",
|
'equal': "equal to",
|
||||||
'not_equal': "not equal to",
|
'not_equal': "not equal to",
|
||||||
|
@ -85,13 +97,20 @@ class GridFilter(object):
|
||||||
'does_not_contain': "does not contain",
|
'does_not_contain': "does not contain",
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, key, label=None, verbs=None, renderer=None,
|
valueless_verbs = ['is_any', 'is_null', 'is_not_null', 'is_true', 'is_false']
|
||||||
|
|
||||||
|
value_renderer_factory = DefaultValueRenderer
|
||||||
|
|
||||||
|
def __init__(self, key, label=None, verbs=None, value_renderer=None,
|
||||||
default_active=False, default_verb=None, default_value=None):
|
default_active=False, default_verb=None, default_value=None):
|
||||||
self.key = key
|
self.key = key
|
||||||
self.label = label or prettify(key)
|
self.label = label or prettify(key)
|
||||||
self.verbs = verbs or self.get_default_verbs()
|
self.verbs = verbs or self.get_default_verbs()
|
||||||
self.renderer = renderer or DefaultRenderer()
|
if value_renderer is not None:
|
||||||
self.renderer.filter = self
|
value_renderer.filter = self
|
||||||
|
self.value_renderer = value_renderer
|
||||||
|
else:
|
||||||
|
self.value_renderer = self.value_renderer_factory(self)
|
||||||
self.default_active = default_active
|
self.default_active = default_active
|
||||||
self.default_verb = default_verb
|
self.default_verb = default_verb
|
||||||
self.default_value = default_value
|
self.default_value = default_value
|
||||||
|
@ -132,9 +151,14 @@ class GridFilter(object):
|
||||||
"""
|
"""
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def render(self, **kwargs):
|
def render_value(self, value=UNSPECIFIED, **kwargs):
|
||||||
kwargs['filter'] = self
|
"""
|
||||||
return self.renderer.render(**kwargs)
|
Render the HTML needed to expose the filter's value for user input.
|
||||||
|
"""
|
||||||
|
if value is UNSPECIFIED:
|
||||||
|
value = self.value
|
||||||
|
kwargs['filtr'] = self
|
||||||
|
return self.value_renderer.render(value=value, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class AlchemyGridFilter(GridFilter):
|
class AlchemyGridFilter(GridFilter):
|
||||||
|
@ -284,6 +308,25 @@ class AlchemyBooleanFilter(AlchemyGridFilter):
|
||||||
return query.filter(self.column == False)
|
return query.filter(self.column == False)
|
||||||
|
|
||||||
|
|
||||||
|
class AlchemyDateFilter(AlchemyGridFilter):
|
||||||
|
"""
|
||||||
|
Date filter for SQLAlchemy.
|
||||||
|
"""
|
||||||
|
value_renderer_factory = DateValueRenderer
|
||||||
|
|
||||||
|
def filter_is_true(self, query, value):
|
||||||
|
"""
|
||||||
|
Filter data with an "is true" query (alias for "is not null").
|
||||||
|
"""
|
||||||
|
return self.filter_is_not_null(query, value)
|
||||||
|
|
||||||
|
def filter_is_false(self, query, value):
|
||||||
|
"""
|
||||||
|
Filter data with an "is false" query (alias for "is null").
|
||||||
|
"""
|
||||||
|
return self.filter_is_null(query, value)
|
||||||
|
|
||||||
|
|
||||||
class GridFilterSet(OrderedDict):
|
class GridFilterSet(OrderedDict):
|
||||||
"""
|
"""
|
||||||
Collection class for :class:`GridFilter` instances.
|
Collection class for :class:`GridFilter` instances.
|
||||||
|
@ -337,13 +380,18 @@ class GridFiltersFormRenderer(FormRenderer):
|
||||||
"""
|
"""
|
||||||
Render the verb selection dropdown for the given filter.
|
Render the verb selection dropdown for the given filter.
|
||||||
"""
|
"""
|
||||||
options = [(v, filtr.verbmap.get(v, "unknown verb '{0}'".format(v)))
|
options = [(v, filtr.verb_labels.get(v, "unknown verb '{0}'".format(v)))
|
||||||
for v in filtr.verbs]
|
for v in filtr.verbs]
|
||||||
return self.select('{0}.verb'.format(filtr.key), options, class_='verb')
|
hide_values = [v for v in filtr.valueless_verbs
|
||||||
|
if v in filtr.verbs]
|
||||||
|
return self.select('{0}.verb'.format(filtr.key), options, **{
|
||||||
|
'class_': 'verb',
|
||||||
|
'data-hide-value-for': ' '.join(hide_values)})
|
||||||
|
|
||||||
def filter_value(self, filtr):
|
def filter_value(self, filtr, **kwargs):
|
||||||
"""
|
"""
|
||||||
Render the value input element(s) for the filter.
|
Render the value input element(s) for the filter.
|
||||||
"""
|
"""
|
||||||
# TODO: This surely needs some work..?
|
style = 'display: none;' if filtr.verb in filtr.valueless_verbs else None
|
||||||
return HTML.tag('div', class_='value', c=filtr.render(value=self.value(filtr.key)))
|
return HTML.tag('div', class_='value', style=style,
|
||||||
|
c=filtr.render_value(**kwargs))
|
||||||
|
|
26
tailbone/static/js/jquery.ui.tailbone.js
vendored
26
tailbone/static/js/jquery.ui.tailbone.js
vendored
|
@ -213,6 +213,8 @@
|
||||||
|
|
||||||
_create: function() {
|
_create: function() {
|
||||||
|
|
||||||
|
var that = this;
|
||||||
|
|
||||||
// Track down some important elements.
|
// Track down some important elements.
|
||||||
this.checkbox = this.element.find('input[name$="-active"]');
|
this.checkbox = this.element.find('input[name$="-active"]');
|
||||||
this.label = this.element.find('label');
|
this.label = this.element.find('label');
|
||||||
|
@ -229,8 +231,24 @@
|
||||||
icons: {primary: 'ui-icon-blank'}
|
icons: {primary: 'ui-icon-blank'}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Enhance some more stuff.
|
// Enhance verb dropdown as selectmenu.
|
||||||
this.inputs.find('.verb').selectmenu({width: '15em'});
|
this.verb_select = this.inputs.find('.verb');
|
||||||
|
this.valueless_verbs = {};
|
||||||
|
$.each(this.verb_select.data('hide-value-for').split(' '), function(index, value) {
|
||||||
|
that.valueless_verbs[value] = true;
|
||||||
|
});
|
||||||
|
this.verb_select.selectmenu({
|
||||||
|
width: '15em',
|
||||||
|
change: function(event, ui) {
|
||||||
|
if (ui.item.value in that.valueless_verbs) {
|
||||||
|
that.inputs.find('.value').hide();
|
||||||
|
} else {
|
||||||
|
that.inputs.find('.value').show();
|
||||||
|
that.focus();
|
||||||
|
that.select();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Listen for button click, to keep checkbox in sync.
|
// Listen for button click, to keep checkbox in sync.
|
||||||
this._on(this.activebutton, {
|
this._on(this.activebutton, {
|
||||||
|
@ -289,6 +307,10 @@
|
||||||
this.inputs.find('.value input').focus();
|
this.inputs.find('.value input').focus();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
select: function() {
|
||||||
|
this.inputs.find('.value input').select();
|
||||||
|
},
|
||||||
|
|
||||||
value: function() {
|
value: function() {
|
||||||
return this.inputs.find('.value input').val();
|
return this.inputs.find('.value input').val();
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue