[FIX] greatly simplify widgets-rendering routine of search view (basically remove all the rendering part), add basic drawer behavior

bzr revid: xmo@openerp.com-20120326145017-g1ydm1k93hipy2j5
This commit is contained in:
Xavier Morel 2012-03-26 16:50:17 +02:00
parent f97751c63e
commit c21f00ab81
5 changed files with 277 additions and 75 deletions

View File

@ -57,14 +57,14 @@
color: white;
-moz-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
-webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; }
box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; }
.openerp2 .oe_topbar .oe_topbar_item .oe_active {
background: #303030;
font-weight: bold;
color: white;
-moz-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
-webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; }
box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; }
.openerp2 .oe_topbar .oe_topbar_avatar {
width: 24px;
height: 24px;
@ -128,14 +128,14 @@
color: white;
-moz-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
-webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; }
box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; }
.openerp2 .oe_menu .oe_active {
background: #303030;
font-weight: bold;
color: white;
-moz-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
-webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; }
box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; }
.openerp2 .oe_secondary_menu_section {
font-weight: bold;
margin-left: 8px;
@ -169,7 +169,7 @@
border-radius: 4px;
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2);
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2);
-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2); }
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2); }
.openerp2 .oe_secondary_submenu .oe_active {
background: #8a89ba;
border-top: 1px solid lightGray;
@ -177,7 +177,7 @@
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2);
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2);
-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2); }
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2); }
.openerp2 .oe_secondary_submenu .oe_active a {
color: white; }
.openerp2 .oe_secondary_submenu .oe_active .oe_menu_label {
@ -186,7 +186,7 @@
text-shadow: 0 1px 1px white;
-moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); }
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); }
.openerp2 .oe_secondary_submenu .oe_menu_toggler:before {
width: 0;
height: 0;
@ -255,7 +255,7 @@
border-radius: 0 0 6px 6px;
-moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
-webkit-background-clip: padding-box;
-moz-background-clip: padding-box;
background-clip: padding-box; }
@ -280,4 +280,52 @@
background-image: linear-gradient(to bottom, #292929, #191919);
-moz-box-shadow: none;
-webkit-box-shadow: none;
-box-shadow: none; }
box-shadow: none; }
.openerp2 .oe_searchview {
position: relative; }
.openerp2 .oe_searchview .VS-search .VS-search-inner {
margin-right: 40px; }
.openerp2 .oe_searchview .VS-search .VS-icon-cancel {
right: 24px; }
.openerp2 .oe_searchview .VS-search .oe_vs_unfold_drawer {
position: absolute;
top: 0;
right: 0;
height: 100%;
line-height: 28px;
padding: 0 7px 0 4px;
color: #cccccc;
cursor: pointer; }
.openerp2 .oe_searchview .VS-search .oe_vs_unfold_drawer:before {
content: "▼"; }
.openerp2 .oe_searchview.oe_searchview_open_drawer .oe_searchview_drawer {
display: table; }
.openerp2 .oe_searchview .oe_searchview_drawer {
position: absolute;
top: 100%;
right: 0;
background-color: white;
width: 432px;
display: none;
border: 1px solid #cccccc; }
.openerp2 .oe_searchview .oe_searchview_drawer > div {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
display: table-cell;
width: 50%; }
.openerp2 .oe_searchview .oe_searchview_drawer h3 {
margin: 2px 4px 2px 8px;
color: #8786b7; }
.openerp2 .oe_searchview .oe_searchview_drawer ul {
margin: 0 12px 3px;
padding: 0;
list-style: none; }
.openerp2 .oe_searchview .oe_searchview_drawer li {
list-style: none;
padding: 3px 6px;
height: 14px;
color: inherit;
cursor: pointer; }
.openerp2 .oe_searchview .oe_searchview_drawer li:hover {
background-color: #f0f0fa; }

View File

@ -1,4 +1,5 @@
// Variables {{{
$section-title-color: #8786b7
$colour4: #8a89ba
//$colour4: #d14836
// }}}
@ -29,7 +30,7 @@ $colour4: #8a89ba
@mixin box-shadow($bsval: 0px 1px 4px #777)
-moz-box-shadow: $bsval
-webkit-box-shadow: $bsval
-box-shadow: $bsval
box-shadow: $bsval
@mixin transition($transval: (border linear 0.2s, box-shadow linear 0.2s))
-webkit-transition: $transval
@ -46,6 +47,12 @@ $colour4: #8a89ba
-webkit-background-clip: $clip
-moz-background-clip: $clip
background-clip: $clip
@mixin unscrew-box
-moz-box-sizing: border-box
-webkit-box-sizing: border-box
box-sizing: border-box
// }}}
.openerp2
@ -322,6 +329,67 @@ $colour4: #8a89ba
@include box-shadow(none)
// }}}
// Search View {{{
.oe_searchview
position: relative
.VS-search
.VS-search-inner
margin-right: 40px
.VS-icon-cancel
right: 24px
.oe_vs_unfold_drawer
position: absolute
top: 0
right: 0
height: 100%
line-height: 28px
padding: 0 7px 0 4px
color: #ccc
cursor: pointer
&:before
content: ""
&.oe_searchview_open_drawer
.oe_searchview_drawer
display: table
.oe_searchview_drawer
position: absolute
top: 100%
right: 0
background-color: white
width: 432px
display: none
border: 1px solid #ccc
> div
@include unscrew-box
display: table-cell
width: 50%
h3
margin: 2px 4px 2px 8px
color: $section-title-color
ul
margin: 0 12px 3px
padding: 0
list-style: none
li
list-style: none
padding: 3px 6px
height: 14px
color: inherit
cursor: pointer
&:hover
background-color: #f0f0fa
// }}}
.openerp
// Transitional overrides for old styles {{{
// }}}

View File

@ -2,6 +2,9 @@ openerp.web.search = function(openerp) {
var QWeb = openerp.web.qweb,
_t = openerp.web._t,
_lt = openerp.web._lt;
_.mixin({
sum: function (obj) { return _.reduce(obj, function (a, b) { return a + b; }, 0); }
});
// Have SearchBox optionally use callback function to produce inputs and facets
// (views) set on callbacks.make_facet and callbacks.make_input keys when
@ -48,7 +51,7 @@ var SearchBox_searchEvent = function (e) {
};
if (SearchBox_searchEvent.toString() !== VS.ui.SearchBox.prototype.searchEvent.toString().replace(
/this\.focusSearch\(e\);\n[ ]{4}this\.value\(query\)/,
'this\.renderFacets();\n this\.focusSearch(e)')) {
'this.renderFacets();\n this.focusSearch(e)')) {
throw new Error(
"Trying to replace wrong version of VS.ui.SearchBox#searchEvent. "
+ "Please fix replacement.");
@ -89,8 +92,7 @@ openerp.web.SearchView = openerp.web.Widget.extend(/** @lends openerp.web.Search
this.has_defaults = !_.isEmpty(this.defaults);
this.inputs = [];
this.has_focus = false;
this.controls = {};
this.hidden = !!hidden;
this.headless = this.hidden && !this.has_defaults;
@ -130,6 +132,11 @@ openerp.web.SearchView = openerp.web.Widget.extend(/** @lends openerp.web.Search
context: this.dataset.get_context()
}, this.on_loaded);
}
this.$element.on('click', '.oe_vs_unfold_drawer', function () {
self.$element.toggleClass('oe_searchview_open_drawer');
});
return $.when(p, this.ready);
},
show: function () {
@ -139,6 +146,64 @@ openerp.web.SearchView = openerp.web.Widget.extend(/** @lends openerp.web.Search
this.$element.hide();
},
/**
* Sets up thingie where all the mess is put?
*/
setup_stuff_drawer: function () {
$('<div class="oe_vs_unfold_drawer">').appendTo(this.$element.find('.VS-search-box'));
var $drawer = $('<div class="oe_searchview_drawer">').appendTo(this.$element);
var running_count = 0;
// get total filters count
var is_group = function (i) { return i instanceof openerp.web.search.FilterGroup; };
var filters_count = _(this.controls).chain()
.flatten()
.filter(is_group)
.map(function (i) { return i.filters.length; })
.sum()
.value();
var col1 = [], col2 = _(this.controls).map(function (inputs, group) {
var filters = _(inputs).filter(is_group);
return {
name: group === 'null' ? _t("Filters") : group,
filters: filters,
length: _(filters).chain().map(function (i) {
return i.filters.length; }).sum().value()
};
});
while (col2.length) {
// col1 + group should be smaller than col2 + group
if ((running_count + col2[0].length) <= (filters_count - running_count)) {
running_count += col2[0].length;
col1.push(col2.shift());
} else {
break;
}
}
[].push.call(col1.length <= col2.length ? col1 : col2, {
name: "Custom Filters",
filters: [new openerp.web.search.FilterGroup([
{attrs: {string: "Custom 0"}},
{attrs: {string: "Custom 1"}},
{attrs: {string: "Custom 2"}}
], this)],
length: 3
});
return $.when(
this.render_column(col1, $('<div>').appendTo($drawer)),
this.render_column(col2, $('<div>').appendTo($drawer)));
},
render_column: function (column, $el) {
return $.when.apply(null, _(column).map(function (group) {
$('<h3>').text(group.name).appendTo($el);
return $.when.apply(null,
_(group.filters).invoke('appendTo', $el));
}));
},
/**
* Sets up search view's view-wide auto-completion widget
*/
@ -246,56 +311,41 @@ openerp.web.SearchView = openerp.web.Widget.extend(/** @lends openerp.web.Search
*
* @param {Array} items a list of nodes to convert to widgets
* @param {Object} fields a mapping of field names to (ORM) field attributes
* @returns Array
* @param {String} [group_name] name of the group to put the new controls in
*/
make_widgets: function (items, fields) {
var rows = [],
row = [];
rows.push(row);
make_widgets: function (items, fields, group_name) {
group_name = group_name || null;
if (!(group_name in this.controls)) {
this.controls[group_name] = [];
}
var self = this, group = this.controls[group_name];
var filters = [];
_.each(items, function (item) {
if (filters.length && item.tag !== 'filter') {
row.push(
new openerp.web.search.FilterGroup(
filters, this));
group.push(new openerp.web.search.FilterGroup(filters, this));
filters = [];
}
if (item.tag === 'newline') {
row = [];
rows.push(row);
} else if (item.tag === 'filter') {
if (!this.has_focus) {
item.attrs.default_focus = '1';
this.has_focus = true;
}
filters.push(
new openerp.web.search.Filter(
item, this));
} else if (item.tag === 'separator') {
// a separator is a no-op
} else {
if (item.tag === 'group') {
// TODO: group and field should be fetched from registries, maybe even filters
row.push(
new openerp.web.search.Group(
item, this, fields));
} else if (item.tag === 'field') {
if (!this.has_focus) {
item.attrs.default_focus = '1';
this.has_focus = true;
}
row.push(
this.make_field(
item, fields[item['attrs'].name]));
}
switch (item.tag) {
case 'separator': case 'newline':
break;
case 'filter':
filters.push(new openerp.web.search.Filter(item, this));
break;
case 'group':
self.make_widgets(item.children, fields, item.attrs.string);
break;
case 'field':
group.push(this.make_field(item, fields[item['attrs'].name]));
// filters
self.make_widgets(item.children, fields, group_name);
break;
}
}, this);
if (filters.length) {
row.push(new openerp.web.search.FilterGroup(filters, this));
}
return rows;
if (filters.length) {
group.push(new openerp.web.search.FilterGroup(filters, this));
}
},
/**
* Creates a field for the provided field descriptor item (which comes
@ -334,15 +384,15 @@ openerp.web.SearchView = openerp.web.Widget.extend(/** @lends openerp.web.Search
}
this.make_widgets(
data.fields_view['arch'].children,
data.fields_view.fields);
data.fields_view['arch'].children,
data.fields_view.fields);
// load defaults
return $.when.apply(null, _(this.inputs).invoke('facet_for_defaults', this.defaults))
.then(function () {
self.vs.searchQuery.reset(_(arguments).compact());
self.ready.resolve();
});
return $.when(
this.setup_stuff_drawer(),
$.when.apply(null, _(this.inputs).invoke('facet_for_defaults', this.defaults))
.then(function () { self.vs.searchQuery.reset(_(arguments).compact()); }))
.then(function () { self.ready.resolve(); })
},
reload_managed_filters: function() {
var self = this;
@ -717,6 +767,10 @@ openerp.web.search.FilterGroup = openerp.web.search.Input.extend(/** @lends open
this._super(view);
this.filters = filters;
},
start: function () {
this.$element.on('click', 'li', this.proxy('toggle_filter'));
return $.when(null);
},
facet_for_defaults: function (defaults) {
var fs = _(this.filters).filter(function (f) {
return f.attrs && f.attrs.name && !!defaults[f.attrs.name];
@ -769,6 +823,38 @@ openerp.web.search.FilterGroup = openerp.web.search.Input.extend(/** @lends open
return _.extend(new openerp.web.CompoundDomain(), {
__domains: domains
});
},
toggle_filter: function (e) {
var self = this, fs;
var filter = this.filters[$(e.target).index()];
var facet = this.view.vs.searchQuery.detect(function (f) {
return f.get('field') === self; });
if (facet) {
fs = facet.get('json');
this.view.vs.searchQuery.remove(facet);
if (_.include(fs, filter)) {
fs = _.without(fs, filter);
} else {
fs.push(filter);
}
if (_(fs).isEmpty()) {
this.view.vs.searchBox.renderFacets();
return;
}
} else {
fs = [filter];
}
this.view.vs.searchQuery.add({
category: 'q',
value: _(fs).map(function (f) {
return f.attrs.string || f.attrs.name }).join(' | '),
json: fs,
field: this,
app: this.view.vs
});
}
});
openerp.web.search.Filter = openerp.web.search.Input.extend(/** @lends openerp.web.search.Filter# */{
@ -809,17 +895,6 @@ openerp.web.search.Field = openerp.web.search.Input.extend( /** @lends openerp.w
init: function (view_section, field, view) {
this._super(view);
this.load_attrs(_.extend({}, field, view_section.attrs));
this.filters = new openerp.web.search.FilterGroup(_.compact(_.map(
view_section.children, function (filter_node) {
if (filter_node.attrs.string &&
typeof console !== 'undefined' && console.debug) {
console.debug("Filter-in-field with a 'string' attribute "
+ "in view", view);
}
delete filter_node.attrs.string;
return new openerp.web.search.Filter(
filter_node, view);
})), view);
},
facet_for: function (value) {
return $.when(new VS.model.SearchFacet({

View File

@ -1299,10 +1299,11 @@
<br t-if="attrs.string"/>
<t t-esc="attrs.string"/>
</button>
<span t-name="SearchView.filters" class="filter_label_group"
><t t-foreach="filters" t-as="filter"
><t t-raw="filter.render(defaults)"/></t
></span>
<ul t-name="SearchView.filters">
<li t-foreach="widget.filters" t-as="filter">
<t t-esc="filter.attrs.string or filter.attrs.name or 'Ω'"/>
</li>
</ul>
<t t-name="SearchView.field">
<label t-att-class="'oe_label' + (attrs.help ? '_help' : '')"
t-att-title="attrs.help"

View File

@ -259,6 +259,9 @@ Filters
* :js:func:`openerp.web.search.Filter.is_enabled` has been removed
* :js:class:`~openerp.web.search.FilterGroup` instances are still
rendered (and started) in the "advanced search" drawer".
Fields
++++++
@ -276,6 +279,13 @@ Fields
have all the information they need to derive the "right" resulting
domain.
Custom filters
++++++++++++++
Instead of being an intrinsic part of the search view, custom filters
are now a special case of filter groups. They are treated specially
still, but much less so than they used to be.
Many To One
+++++++++++