[facets] do no stretch to the right when there are many facets; instead use a floating layout (closes #2093160)
authorAurelien Campeas <aurelien.campeas@logilab.fr>
Wed, 23 Nov 2011 12:30:05 +0100
changeset 8093 3efb83e4e8f3
parent 8092 bc2a7c9fe266
child 8094 bedf36fb17f1
[facets] do no stretch to the right when there are many facets; instead use a floating layout (closes #2093160)
uilib.py
web/data/cubicweb.facets.css
web/data/uiprops.py
web/facet.py
web/views/facets.py
--- a/uilib.py	Wed Nov 23 11:46:30 2011 +0100
+++ b/uilib.py	Wed Nov 23 12:30:05 2011 +0100
@@ -144,6 +144,22 @@
 def printable_value(req, attrtype, value, props=None, displaytime=True):
     return req.printable_value(attrtype, value, props, displaytime)
 
+def css_em_num_value(vreg, propname, default):
+    """ we try to read an 'em' css property
+    if we get another unit we're out of luck and resort to the given default
+    (hence, it is strongly advised not to specify but ems for this css prop)
+    """
+    propvalue = vreg.config.uiprops[propname].lower().strip()
+    if propvalue.endswith('em'):
+        try:
+            return float(propvalue[:-2])
+        except Exception:
+            vreg.warning('css property %s looks malformed (%r)',
+                         propname, propvalue)
+    else:
+        vreg.warning('css property %s should use em (currently is %r)',
+                     propname, propvalue)
+    return default
 
 # text publishing #############################################################
 
--- a/web/data/cubicweb.facets.css	Wed Nov 23 11:46:30 2011 +0100
+++ b/web/data/cubicweb.facets.css	Wed Nov 23 12:30:05 2011 +0100
@@ -3,15 +3,22 @@
  padding: 0px;
 }
 
-div.facet {
- background: #fff;
- padding: 5px;
- margin: .3em!important;
+.facet {
+  border: 1px solid chocolate;
+  background: #fff;
+  padding: %(facet_Padding)s;
+  margin-bottom: %(facet_MarginBottom)s;
+}
+
+.facetGroup {
+  margin: .3em;
+  float: left;
+  max-height: %(facet_Height)s;
 }
 
 div.facetTitle, div.bkSearch  {
  color: #000;
- margin-bottom: 2px;
+ margin: 2px;
  cursor: pointer;
  font-weight: bold;
  font: %(facet_titleFont)s;
@@ -26,15 +33,19 @@
  color: #000 !important;
 }
 
-div.vocabularyFacetBody {
-  height: %(facet_overflowedHeight)s;
+.facetGroup div.rangeFacet {
+  width: 13em;
 }
 
-div.vocabularyFacetBodyWithLogicalSelector {
-  height: %(facet_overflowedHeightWithLogicalSelector)s;
+.facetGroup div.vocabularyFacet {
+  /* when facets spread on several lines, it can relieve the eye
+     to have them vertically aligned; these properties should
+     be used then  */
+  /* width: 13em; */
 }
 
 div.vocabularyFacet {
+  max-height: %(facet_vocabMaxHeight)s;
   overflow-y: auto;
   overflow-x: hidden;
 }
@@ -72,10 +83,10 @@
   border: none;
 }
 
-.facet input{
- margin-top:3px;
- border:1px solid #ccc;
- font-size:11px;
+.facet input {
+  margin-top: .2em;
+  border: 1px solid #ccc;
+  font-size: small;
 }
 
 .facetValueDisabled span {
@@ -108,7 +119,8 @@
   background: url("required.png") no-repeat right top;
 }
 
-table.filter {
+.filter {
   background-color: #EBE8D9;
   border: dotted grey 1px;
+  display: inline-block;
 }
--- a/web/data/uiprops.py	Wed Nov 23 11:46:30 2011 +0100
+++ b/web/data/uiprops.py	Wed Nov 23 12:30:05 2011 +0100
@@ -168,6 +168,7 @@
 
 # facets
 facet_titleFont = 'bold SansSerif'
-facet_overflowedHeight = '12em'
-# the above minus 1
-facet_overflowedHeightWithLogicalSelector = '11em'
+facet_Height = '15em'
+facet_Padding = '.4em'
+facet_MarginBottom = '.4em'
+facet_vocabMaxHeight = '12em' # ensure << facet_Height
--- a/web/facet.py	Wed Nov 23 11:46:30 2011 +0100
+++ b/web/facet.py	Wed Nov 23 12:30:05 2011 +0100
@@ -55,7 +55,7 @@
 
 from logilab.mtconverter import xml_escape
 from logilab.common.graph import has_path
-from logilab.common.decorators import cached
+from logilab.common.decorators import cached, cachedproperty
 from logilab.common.date import datetime2ticks, ustrftime, ticks2datetime
 from logilab.common.compat import all
 from logilab.common.deprecation import deprecated
@@ -64,6 +64,7 @@
 
 from cubicweb import Unauthorized, typed_eid
 from cubicweb.schema import display_name
+from cubicweb.uilib import css_em_num_value
 from cubicweb.utils import make_uid
 from cubicweb.selectors import match_context_prop, partial_relation_possible, yes
 from cubicweb.appobject import AppObject
@@ -1406,7 +1407,7 @@
 
 
 ## html widets ################################################################
-_DEFAULT_CONSTANT_VOCAB_WIDGET_HEIGHT = 12
+_DEFAULT_CONSTANT_VOCAB_WIDGET_HEIGHT = 14
 
 class FacetVocabularyWidget(htmlwidgets.HTMLWidget):
 
@@ -1414,28 +1415,21 @@
         self.facet = facet
         self.items = []
 
-    @property
-    @cached
+    @cachedproperty
     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
+        return css_em_num_value(self.facet._cw.vreg, 'facet_vocabMaxHeight',
+                                _DEFAULT_CONSTANT_VOCAB_WIDGET_HEIGHT)
 
-    @property
-    @cached
+    @cachedproperty
     def height(self):
-        return 1 + min(len(self.items),
-                       self.css_overflow_limit + int(self.facet._support_and_compat()))
+        """ title, optional and/or dropdown, len(items) or upper limit """
+        return (1.5 + # title + small magic constant
+                int(self.facet._support_and_compat() +
+                    min(len(self.items), self.css_overflow_limit)))
 
     @property
     @cached
@@ -1499,7 +1493,7 @@
 
     @property
     def height(self):
-        return 3
+        return 2.5
 
     def _render(self):
         w = self.w
@@ -1544,7 +1538,7 @@
 
     @property
     def height(self):
-        return 3
+        return 2.5
 
     def _render(self):
         w = self.w
@@ -1564,7 +1558,7 @@
             })
         title = xml_escape(self.facet.title)
         facetname = xml_escape(facetname)
-        w(u'<div id="%s" class="facet">\n' % facetid)
+        w(u'<div id="%s" class="facet rangeFacet">\n' % facetid)
         w(u'<div class="facetTitle" cubicweb:facetName="%s">%s</div>\n' %
           (facetname, title))
         cssclass = 'facetBody'
@@ -1615,7 +1609,7 @@
 
     @property
     def height(self):
-        return 2
+        return 1.5
 
     def _render(self):
         w = self.w
@@ -1641,13 +1635,6 @@
         w(u'</div>\n')
 
 
-class FacetSeparator(htmlwidgets.HTMLWidget):
-    def __init__(self, label=None):
-        self.label = label or u'&#160;'
-
-    def _render(self):
-        pass
-
 # other classes ################################################################
 
 class FilterRQLBuilder(object):
--- a/web/views/facets.py	Wed Nov 23 11:46:30 2011 +0100
+++ b/web/views/facets.py	Wed Nov 23 12:30:05 2011 +0100
@@ -23,11 +23,14 @@
 from warnings import warn
 
 from logilab.mtconverter import xml_escape
+from logilab.common.decorators import cachedproperty
 
 from cubicweb.appobject import objectify_selector
 from cubicweb.selectors import (non_final_entity, multi_lines_rset,
                                 match_context_prop, yes, relation_possible)
 from cubicweb.utils import json_dumps
+from cubicweb.uilib import css_em_num_value
+from cubicweb.view import AnyRsetView
 from cubicweb.web import component, facet as facetbase
 
 def facets(req, rset, context, mainvar=None, **kwargs):
@@ -199,7 +202,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: 99 * (not x.facet.start_unfolded) or x.height )
 
     def layout_widgets(self, w, wdgs):
         """layout widgets: by default simply render each of them
@@ -266,12 +269,10 @@
         return self.bk_linkbox_template % bk_link
 
 
-from cubicweb.view import AnyRsetView
-
 class FilterTable(FacetFilterMixIn, AnyRsetView):
     __regid__ = 'facet.filtertable'
     __select__ = has_facets()
-    compact_layout_threshold = 5
+    average_perfacet_uncomputable_overhead = .3
 
     def call(self, vid, divid, vidargs=None, cssclass=''):
         hiddens = self.cw_extra_kwargs.setdefault('hiddens', {})
@@ -279,47 +280,39 @@
         self.generate_form(self.w, self.cw_rset, divid, vid, vidargs=vidargs,
                            cssclass=cssclass, **self.cw_extra_kwargs)
 
-    def _simple_horizontal_layout(self, w, wdgs):
-        w(u'<table class="filter">\n')
-        w(u'<tr>\n')
-        for wdg in wdgs:
-            w(u'<td>')
-            wdg.render(w=w)
-            w(u'</td>')
-        w(u'</tr>\n')
-        w(u'</table>\n')
+    @cachedproperty
+    def per_facet_height_overhead(self):
+        return (css_em_num_value(self._cw.vreg, 'facet_MarginBottom', .2) +
+                css_em_num_value(self._cw.vreg, 'facet_Padding', .2) +
+                self.average_perfacet_uncomputable_overhead)
 
     def layout_widgets(self, w, wdgs):
         """layout widgets: put them in a table where each column should have
         sum(wdg.height) < wdg_stack_size.
         """
-        if len(wdgs) < self.compact_layout_threshold:
-            self._simple_horizontal_layout(w, wdgs)
-            return
-        w(u'<table class="filter">\n')
+        w(u'<div class="filter">\n')
         widget_queue = []
         queue_height = 0
-        wdg_stack_size = max(wdgs, key=lambda wdg:wdg.height).height
-        w(u'<tr>\n')
+        wdg_stack_size = css_em_num_value(self._cw.vreg, 'facet_Height',
+                                          facetbase._DEFAULT_CONSTANT_VOCAB_WIDGET_HEIGHT)
         for wdg in wdgs:
-            height = wdg.height
+            height = wdg.height + self.per_facet_height_overhead
             if queue_height + height <= wdg_stack_size:
                 widget_queue.append(wdg)
                 queue_height += height
                 continue
-            w(u'<td>')
+            w(u'<div class="facetGroup">')
             for queued in widget_queue:
                 queued.render(w=w)
-            w(u'</td>')
+            w(u'</div>')
             widget_queue = [wdg]
             queue_height = height
         if widget_queue:
-            w(u'<td>')
+            w(u'<div class="facetGroup">')
             for queued in widget_queue:
                 queued.render(w=w)
-            w(u'</td>')
-        w(u'</tr>\n')
-        w(u'</table>\n')
+            w(u'</div>')
+        w(u'</div>\n')
 
 
 # facets ######################################################################