# HG changeset patch # User Aurelien Campeas # Date 1318490726 -7200 # Node ID ad0581296e2cff7ef6d536ed3e2c34d50c635015 # Parent d12c21ea4cd4deb6a1f49ba634fbe3a541f791ea [facets] try to get rid of arbitrary constants, be prettier and eat less space (closes #1988706) diff -r d12c21ea4cd4 -r ad0581296e2c web/data/cubicweb.facets.css --- a/web/data/cubicweb.facets.css Thu Oct 13 09:15:16 2011 +0200 +++ b/web/data/cubicweb.facets.css Thu Oct 13 09:25:26 2011 +0200 @@ -4,17 +4,16 @@ } div.facet { - margin-bottom: 8px; background: #fff; padding: 5px; - min-width: 10em; + margin: .3em!important; } div.facetTitle, div.bkSearch { - font-size: 80%; color: #000; margin-bottom: 2px; cursor: pointer; + font-weight: bold; font: %(facet_titleFont)s; } @@ -23,45 +22,41 @@ background: transparent url("puce.png") 0% 50% no-repeat; } -div.facetBody { -} - -.opened{ +.opened { color: #000 !important; } -div.overflowed { +div.vocabularyFacetBody { height: %(facet_overflowedHeight)s; +} + +div.vocabularyFacetBodyWithLogicalSelector { + height: %(facet_overflowedHeightWithLogicalSelector)s; +} + +div.vocabularyFacet { overflow-y: auto; + overflow-x: hidden; } div.facetCheckBox { clear: both; cursor: pointer; -} - -div.facetCheckBox a { - text-decoration: none; - font-size: 85%; + text-decoration: none; } div.facetValue{ -clear: both -} - -div.facetValue img{ - float: left; - background: #fff; + padding-left: 2px; + clear: both } -div.facetValue a { - margin-left: 20px; - display: block; - margin-top: -6px; /* FIXME why do we need this ? */ +div.facetValue img { + float: left; + background: #fff; } -div.facetValueSelected a { - font-weight: bold; +div.facetValueSelected { + background: #EBE8D9; } #leftcol label { @@ -77,35 +72,26 @@ border: none; } - -div.facetCheckBox{ - line-height:0.8em; - } - .facet input{ margin-top:3px; border:1px solid #ccc; font-size:11px; - } +} - -.facetValueDisabled { +.facetValueDisabled span { font-style: italic; text-decoration: line-through; } - div#filterboxTitle { margin-top: 50px; margin-bottom: 1em; color: #1190A1; font-size: 75%; - font-weight: bold; padding: 0.15em; text-transform: uppercase; } - div#facetLoading { display: none; position: fixed; @@ -126,8 +112,3 @@ background-color: #EBE8D9; border: dotted grey 1px; } - -div.facet { - padding: none; - margin: .3em!important; -} diff -r d12c21ea4cd4 -r ad0581296e2c web/data/cubicweb.widgets.js --- a/web/data/cubicweb.widgets.js Thu Oct 13 09:15:16 2011 +0200 +++ b/web/data/cubicweb.widgets.js Thu Oct 13 09:25:26 2011 +0200 @@ -40,10 +40,6 @@ }); } -// we need to differenciate cases where initFacetBoxEvents is called -// with one argument or without any argument. If we use `initFacetBoxEvents` -// as the direct callback on the jQuery.ready event, jQuery will pass some argument -// of his, so we use this small anonymous function instead. jQuery(document).ready(function() { buildWidgets(); }); diff -r d12c21ea4cd4 -r ad0581296e2c web/data/uiprops.py --- a/web/data/uiprops.py Thu Oct 13 09:15:16 2011 +0200 +++ b/web/data/uiprops.py Thu Oct 13 09:25:26 2011 +0200 @@ -167,7 +167,7 @@ errorMsgColor = '#ed0d0d' # facets -facet_titleFont = 'bold 100% Georgia' +facet_titleFont = 'bold SansSerif' facet_overflowedHeight = '12em' - - +# the above minus 1 +facet_overflowedHeightWithLogicalSelector = '11em' diff -r d12c21ea4cd4 -r ad0581296e2c web/facet.py --- a/web/facet.py Thu Oct 13 09:15:16 2011 +0200 +++ b/web/facet.py Thu Oct 13 09:25:26 2011 +0200 @@ -50,7 +50,7 @@ from warnings import warn from copy import deepcopy -from datetime import date, datetime, timedelta +from datetime import datetime, timedelta from logilab.mtconverter import xml_escape from logilab.common.graph import has_path @@ -59,7 +59,7 @@ from logilab.common.compat import all from logilab.common.deprecation import deprecated -from rql import parse, nodes, utils +from rql import nodes, utils from cubicweb import Unauthorized, typed_eid from cubicweb.schema import display_name @@ -469,6 +469,10 @@ def wdgclass(self): return FacetVocabularyWidget + def get_selected(self): + return frozenset(typed_eid(eid) + for eid in self._cw.list_form_param(self.__regid__)) + def get_widget(self): """Return the widget instance to use to display this facet. @@ -479,12 +483,9 @@ if len(vocab) <= 1: return None wdg = self.wdgclass(self) - selected = frozenset(typed_eid(eid) for eid in self._cw.list_form_param(self.__regid__)) + selected = self.get_selected() for label, value in vocab: - if value is None: - wdg.append(FacetSeparator(label)) - else: - wdg.append(FacetItem(self._cw, label, value, value in selected)) + wdg.items.append((value, label, value in selected)) return wdg def vocabulary(self): @@ -499,7 +500,6 @@ raise NotImplementedError - class RelationFacet(VocabularyFacet): """Base facet to filter some entities according to other entities to which they are related. Create concrete facet by inheriting from this class an then @@ -711,6 +711,7 @@ # internal utilities ####################################################### + @cached def _support_and_compat(self): support = self.support_and if callable(support): @@ -1339,24 +1340,7 @@ ## html widets ################################################################ -_DEFAULT_CONSTANT_VOCAB_WIDGET_HEIGHT = 9 - -@cached -def _css_height_to_line_count(vreg): - cssprop = vreg.config.uiprops['facet_overflowedHeight'].lower().strip() - # let's talk a bit ... - # we try to deduce a number of displayed lines from a css property - # there is a linear (rough empiric coefficient == 0.73) relation between - # css _em_ value and line qty - # if we get another unit we're out of luck and resort to one constant - # hence, it is strongly advised not to specify but ems for this css prop - if cssprop.endswith('em'): - try: - return int(cssprop[:-2]) * .73 - except Exception: - vreg.warning('css property facet_overflowedHeight looks malformed (%r)', - cssprop) - return _DEFAULT_CONSTANT_VOCAB_WIDGET_HEIGHT +_DEFAULT_CONSTANT_VOCAB_WIDGET_HEIGHT = 12 class FacetVocabularyWidget(htmlwidgets.HTMLWidget): @@ -1364,13 +1348,35 @@ self.facet = facet self.items = [] + @property + @cached + def css_overflow_limit(self): + """ we try to deduce a number of displayed lines from a css property + if we get another unit we're out of luck and resort to one constant + hence, it is strongly advised not to specify but ems for this css prop + """ + vreg = self.facet._cw.vreg + cssprop = vreg.config.uiprops['facet_overflowedHeight'].lower().strip() + if cssprop.endswith('em'): + try: + return int(cssprop[:-2]) + except Exception: + vreg.warning('css property facet_overflowedHeight looks malformed (%r)', + cssprop) + return _DEFAULT_CONSTANT_VOCAB_WIDGET_HEIGHT + + @property @cached def height(self): - maxheight = _css_height_to_line_count(self.facet._cw.vreg) - return 1 + min(len(self.items), maxheight) + int(self.facet._support_and_compat()) + return 1 + min(len(self.items), + self.css_overflow_limit + int(self.facet._support_and_compat())) - def append(self, item): - self.items.append(item) + @property + @cached + def overflows(self): + return len(self.items) >= self.css_overflow_limit + + scrollbar_padding_factor = 4 def _render(self): w = self.w @@ -1380,29 +1386,52 @@ w(u'
%s
\n' % (xml_escape(self.facet.__regid__), title)) if self.facet._support_and_compat(): - _ = self.facet._cw._ - w(u'''''' % (xml_escape(self.facet.__regid__) + '_andor', _('and/or between different values'), - _('OR'), _('AND'))) - cssclass = 'facetBody' + self._render_and_or(w) + cssclass = 'vocabularyFacet' if not self.facet.start_unfolded: cssclass += ' hidden' - if len(self.items) > 6: - cssclass += ' overflowed' + overflow = self.overflows + if overflow: + if self.facet._support_and_compat(): + cssclass += ' vocabularyFacetBodyWithLogicalSelector' + else: + cssclass += ' vocabularyFacetBody' w(u'
\n' % cssclass) - for item in self.items: - item.render(w=w) + for value, label, selected in self.items: + if value is None: + continue + self._render_value(w, value, label, selected, overflow) w(u'
\n') w(u'\n') + def _render_and_or(self, w): + _ = self.facet._cw._ + w(u"""""" % (xml_escape(self.facet.__regid__) + '_andor', + _('and/or between different values'), + _('OR'), _('AND'))) + + def _render_value(self, w, value, label, selected, overflow): + cssclass = 'facetValue facetCheckBox' + if selected: + cssclass += ' facetValueSelected' + w(u'
\n' + % (cssclass, xml_escape(unicode(value)))) + # If it is overflowed one must add padding to compensate for the vertical + # scrollbar; given current css values, 4 blanks work perfectly ... + padding = u' ' * self.scrollbar_padding_factor if overflow else u'' + w('%s' % xml_escape(label)) + w(padding) + w(u'
') class FacetStringWidget(htmlwidgets.HTMLWidget): def __init__(self, facet): self.facet = facet self.value = None + @property def height(self): return 3 @@ -1447,6 +1476,7 @@ self.minvalue = minvalue self.maxvalue = maxvalue + @property def height(self): return 3 @@ -1507,34 +1537,6 @@ facet._cw.html_headers.define_var('DATE_FMT', fmt) -class FacetItem(htmlwidgets.HTMLWidget): - - selected_img = "black-check.png" - unselected_img = "no-check-no-border.png" - - def __init__(self, req, label, value, selected=False): - self._cw = req - self.label = label - self.value = value - self.selected = selected - - def _render(self): - w = self.w - cssclass = 'facetValue facetCheckBox' - if self.selected: - cssclass += ' facetValueSelected' - imgsrc = self._cw.data_url(self.selected_img) - imgalt = self._cw._('selected') - else: - imgsrc = self._cw.data_url(self.unselected_img) - imgalt = self._cw._('not selected') - w(u'
\n' - % (cssclass, xml_escape(unicode(self.value)))) - w(u'%s ' % (imgsrc, imgalt)) - w(u'%s' % xml_escape(self.label)) - w(u'
') - - class CheckBoxFacetWidget(htmlwidgets.HTMLWidget): selected_img = "black-check.png" unselected_img = "black-uncheck.png" @@ -1545,6 +1547,7 @@ self.value = value self.selected = selected + @property def height(self): return 2 @@ -1563,9 +1566,9 @@ imgalt = self._cw._('not selected') w(u'
\n' % (cssclass, xml_escape(unicode(self.value)))) - w(u'
') + w(u'
') w(u'%s ' % (imgsrc, imgalt)) - w(u'' + w(u'' % (xml_escape(self.facet.__regid__), title)) w(u'
\n') w(u'
\n') diff -r d12c21ea4cd4 -r ad0581296e2c web/views/facets.py --- a/web/views/facets.py Thu Oct 13 09:15:16 2011 +0200 +++ b/web/views/facets.py Thu Oct 13 09:25:26 2011 +0200 @@ -178,7 +178,7 @@ """sort widgets: by default sort by widget height, then according to widget.order (the original widgets order) """ - return sorted(wdgs, key=lambda x: x.height()) + return sorted(wdgs, key=lambda x: x.height) def layout_widgets(self, w, wdgs): """layout widgets: by default simply render each of them @@ -276,7 +276,7 @@ def layout_widgets(self, w, wdgs): """layout widgets: put them in a table where each column should have - sum(wdg.height()) < wdg_stack_size. + sum(wdg.height) < wdg_stack_size. """ if len(wdgs) < self.compact_layout_threshold: self._simple_horizontal_layout(w, wdgs) @@ -284,10 +284,10 @@ w(u'\n') widget_queue = [] queue_height = 0 - wdg_stack_size = max(wdgs, key=lambda wdg:wdg.height()).height() + wdg_stack_size = max(wdgs, key=lambda wdg:wdg.height).height w(u'\n') for wdg in wdgs: - height = wdg.height() + height = wdg.height if queue_height + height <= wdg_stack_size: widget_queue.append(wdg) queue_height += height