diff --git a/tailbone/newgrids/filters.py b/tailbone/newgrids/filters.py index 5c14acc1..3b03ddbc 100644 --- a/tailbone/newgrids/filters.py +++ b/tailbone/newgrids/filters.py @@ -43,9 +43,6 @@ class FilterValueRenderer(object): Base class for all filter renderers. """ - def __init__(self, filter=None): - self.filter = filter - @property def name(self): return self.filter.key @@ -69,12 +66,40 @@ class NumericValueRenderer(FilterValueRenderer): Input renderer for numeric values. """ + def render(self, value=None, **kwargs): + return tags.text(self.name, value=value, type='number', **kwargs) + class DateValueRenderer(FilterValueRenderer): """ Input renderer for date values. """ + def render(self, value=None, **kwargs): + return tags.text(self.name, value=value, type='date', **kwargs) + + +class ChoiceValueRenderer(FilterValueRenderer): + """ + Renders value input as a dropdown/selectmenu of available choices. + """ + + def __init__(self, choices): + self.choices = choices + + def render(self, value=None, **kwargs): + return tags.select(self.name, [value], self.choices, **kwargs) + + +class EnumValueRenderer(ChoiceValueRenderer): + """ + Renders value input as a dropdown/selectmenu of available choices. + """ + + def __init__(self, enum): + sorted_keys = sorted(enum, key=lambda k: enum[k].lower()) + self.choices = [(k, enum[k]) for k in sorted_keys] + class GridFilter(object): """ @@ -106,11 +131,7 @@ class GridFilter(object): self.key = key self.label = label or prettify(key) self.verbs = verbs or self.get_default_verbs() - if value_renderer is not None: - value_renderer.filter = self - self.value_renderer = value_renderer - else: - self.value_renderer = self.value_renderer_factory(self) + self.set_value_renderer(value_renderer or self.value_renderer_factory) self.default_active = default_active self.default_verb = default_verb self.default_value = default_value @@ -130,6 +151,15 @@ class GridFilter(object): return verbs return ['equal', 'not_equal', 'is_null', 'is_not_null'] + def set_value_renderer(self, renderer): + """ + Set the value renderer for the filter, post-construction. + """ + if not isinstance(renderer, FilterValueRenderer): + renderer = renderer() + renderer.filter = self + self.value_renderer = renderer + def filter(self, data, verb=None, value=UNSPECIFIED): """ Filter the given data set according to a verb/value pair. If no verb @@ -278,6 +308,7 @@ class AlchemyNumericFilter(AlchemyGridFilter): """ Numeric filter for SQLAlchemy. """ + value_renderer_factory = NumericValueRenderer def default_verbs(self): """ @@ -314,6 +345,13 @@ class AlchemyDateFilter(AlchemyGridFilter): """ value_renderer_factory = DateValueRenderer + def default_verbs(self): + """ + Expose greater-than / less-than verbs in addition to core. + """ + return ['equal', 'not_equal', 'greater_than', 'greater_equal', + 'less_than', 'less_equal', 'is_null', 'is_not_null'] + def filter_is_true(self, query, value): """ Filter data with an "is true" query (alias for "is not null"). diff --git a/tailbone/static/js/jquery.ui.tailbone.js b/tailbone/static/js/jquery.ui.tailbone.js index 0542fe82..af3ace0b 100644 --- a/tailbone/static/js/jquery.ui.tailbone.js +++ b/tailbone/static/js/jquery.ui.tailbone.js @@ -250,6 +250,18 @@ } }); + // Enhance any date values with datepicker widget. + this.inputs.find('.value input[type="date"]').datepicker({ + dateFormat: 'yy-mm-dd', + changeYear: true, + changeMonth: true + }); + + // Enhance any choice/dropdown values with selectmenu. + this.inputs.find('.value select').selectmenu({ + width: '15em' + }); + // Listen for button click, to keep checkbox in sync. this._on(this.activebutton, { click: function(e) { @@ -269,6 +281,11 @@ refresh: function() { if (this.checkbox.is(':checked')) { this.activebutton.button('option', 'icons', {primary: 'ui-icon-check'}); + if (this.verb() in this.valueless_verbs) { + this.inputs.find('.value').hide(); + } else { + this.inputs.find('.value').show(); + } this.inputs.show(); } else { this.activebutton.button('option', 'icons', {primary: 'ui-icon-blank'}); @@ -312,7 +329,7 @@ }, value: function() { - return this.inputs.find('.value input').val(); + return this.inputs.find('.value input, .value select').val(); }, verb: function() {