[facets] try to get rid of arbitrary constants, be prettier and eat less space (closes #1988706)
--- 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;
-}
--- 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();
});
--- 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'
--- 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'<div class="facetTitle" cubicweb:facetName="%s">%s</div>\n' %
(xml_escape(self.facet.__regid__), title))
if self.facet._support_and_compat():
- _ = self.facet._cw._
- w(u'''<select name="%s" class="radio facetOperator" title="%s">
- <option value="OR">%s</option>
- <option value="AND">%s</option>
-</select>''' % (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'<div class="%s">\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'</div>\n')
w(u'</div>\n')
+ def _render_and_or(self, w):
+ _ = self.facet._cw._
+ w(u"""<select name='%s' class='radio facetOperator' title='%s'>
+ <option value='OR'>%s</option>
+ <option value='AND'>%s</option>
+</select>""" % (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'<div class="%s" cubicweb:value="%s">\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('<span>%s</span>' % xml_escape(label))
+ w(padding)
+ w(u'</div>')
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'<div class="%s" cubicweb:value="%s">\n'
- % (cssclass, xml_escape(unicode(self.value))))
- w(u'<img src="%s" alt="%s"/> ' % (imgsrc, imgalt))
- w(u'<a href="javascript: {}">%s</a>' % xml_escape(self.label))
- w(u'</div>')
-
-
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'<div class="%s" cubicweb:value="%s">\n'
% (cssclass, xml_escape(unicode(self.value))))
- w(u'<div class="facetCheckBoxWidget">')
+ w(u'<div>')
w(u'<img src="%s" alt="%s" cubicweb:unselimg="true" /> ' % (imgsrc, imgalt))
- w(u'<label class="facetTitle" cubicweb:facetName="%s"><a href="javascript: {}">%s</a></label>'
+ w(u'<label class="facetTitle" cubicweb:facetName="%s">%s</label>'
% (xml_escape(self.facet.__regid__), title))
w(u'</div>\n')
w(u'</div>\n')
--- 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'<table class="filter">\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'<tr>\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