[IMP] web: use bootstrap's notebook, rewrite process_notebook

Introduction of a specialized InvisibilityChanger for the notebook. It
allows to handle more elegantly the issues of the notebook during
page execution: modifiers hiding the active tab that must now
active its sibling.

Process_notebook only handle the processing of the page while
NotebookInvisibilityChanger only handle the behavior of the notebook
during the page execution.

- readable code (more readable, at least)
- correct handle of autofocus
- rendering of the whole notebook in one call to qweb's render
- one invisibility changer bound by header/content
- no custom css, only legacy bootstrap
This commit is contained in:
Simon Lejeune
2014-12-19 17:49:13 +01:00
parent 01409c1d7b
commit b2e32a8f7e
4 changed files with 92 additions and 174 deletions
+1 -74
View File
@@ -532,7 +532,7 @@
background-image: -webkit-radial-gradient(circle, #eee 0%, #ccc 40%, #bbb 100%);
background-image: -moz-radial-gradient(#eee 0%, #ccc 40%, #bbb 100%);
background-image: -ms-radial-gradient(#eee 0%, #ccc 40%, #bbb 100%);
background-image: radial-gradient(circle, #eee 0%, #ccc 40%, #bbb 100%);
background-image: radial-gradient(circle, #eeeeee 0%, #cccccc 40%, #bbbbbb 100%);
}
.openerp .oe_kanban_status_green {
background: green;
@@ -616,79 +616,6 @@
font-style: italic;
text-decoration: none;
}
.openerp .oe_notebook {
margin: 8px 0;
padding: 0 16px;
list-style: none;
zoom: 1;
}
.openerp .oe_notebook.ui-corner-all {
-moz-border-radius: 0;
-webkit-border-radius: 0;
border-radius: 0;
}
.openerp .oe_notebook:before, .openerp .oe_notebook:after {
display: table;
content: "";
zoom: 1;
}
.openerp .oe_notebook:after {
clear: both;
}
.openerp .oe_notebook > li {
float: left;
}
.openerp .oe_notebook > li > a {
display: block;
color: #808080;
}
.openerp .oe_notebook > li.ui-tabs-active > a {
color: #4c4c4c;
}
.openerp .oe_notebook {
border-color: #ddd;
border-style: solid;
border-width: 0 0 1px;
}
.openerp .oe_notebook > li {
position: relative;
}
.openerp .oe_notebook > li > a {
padding: 0 12px;
margin-right: 2px;
line-height: 30px;
border: 1px solid transparent;
-moz-border-radius: 4px 4px 0 0;
-webkit-border-radius: 4px 4px 0 0;
border-radius: 4px 4px 0 0;
}
.openerp .oe_notebook > li > a:hover {
text-decoration: none;
background-color: #eee;
border-color: #eee #eee #ddd;
}
.openerp .ui-tabs .oe_notebook.ui-tabs-nav li.ui-tabs-active {
border-bottom: none;
padding-bottom: 1px;
}
.openerp .oe_notebook > li.ui-state-active > a, .openerp .oe_notebook > li.ui-state-active > a:hover {
background-color: #ffffff;
border: 1px solid #ddd;
border-bottom-color: transparent;
cursor: default;
}
.openerp .oe_notebook_page {
padding: 0;
}
.openerp .oe_notebook_page > label:not([for]) {
font-weight: normal !important;
}
.openerp div.ui-tabs {
padding: 3px 0px 3px 0px;
}
.openerp .ui-tabs-hide {
display: none;
}
.openerp .oe_dropdown, .openerp .oe_dropdown_hover, .openerp .oe_dropdown_toggle {
position: relative;
cursor: pointer;
-56
View File
@@ -555,62 +555,6 @@ $sheet-padding: 16px
text-decoration: none
margin-bottom: 1px
// }}}
// Notebook {{{
.oe_notebook
margin: 8px 0
padding: 0 16px
list-style: none
zoom: 1
.oe_notebook.ui-corner-all
@include radius(0)
.oe_notebook:before, .oe_notebook:after
display: table
content: ""
zoom: 1
.oe_notebook:after
clear: both
.oe_notebook > li
float: left
.oe_notebook > li > a
display: block
color: #808080
.oe_notebook > li.ui-tabs-active > a
color: #4c4c4c
.oe_notebook
border-color: #ddd
border-style: solid
border-width: 0 0 1px
.oe_notebook > li
position: relative
.oe_notebook > li > a
padding: 0 12px
margin-right: 2px
line-height: 30px
border: 1px solid transparent
@include radius(4px 4px 0 0)
.oe_notebook > li > a:hover
text-decoration: none
background-color: #eee
border-color: #eee #eee #ddd
.ui-tabs .oe_notebook.ui-tabs-nav li.ui-tabs-active
border-bottom: none
padding-bottom: 1px
.oe_notebook > li.ui-state-active > a, .oe_notebook > li.ui-state-active > a:hover
background-color: #ffffff
border: 1px solid #ddd
border-bottom-color: transparent
cursor: default
.oe_notebook_page
padding: 0
//Customize label weight according bootstrap3
> label:not([for])
font-weight: normal !important
// End of customize
div.ui-tabs
padding: 3px 0px 3px 0px
.ui-tabs-hide
display: none
// }}}
// Dropdown {{{
.oe_dropdown, .oe_dropdown_hover, .oe_dropdown_toggle
position: relative
+84 -38
View File
@@ -1514,50 +1514,54 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
},
process_notebook: function($notebook) {
var self = this;
// Extract useful info from the notebook xml declaration
var pages = [];
$notebook.find('> page').each(function() {
var $page = $(this);
var page_attrs = $page.getAttributes();
page_attrs.id = _.uniqueId('notebook_page_');
var $new_page = self.render_element('FormRenderingNotebookPage', page_attrs);
$page.contents().appendTo($new_page);
$page.before($new_page).remove();
var ic = self.handle_common_properties($new_page, $page).invisibility_changer;
page_attrs.__page = $new_page;
page_attrs.__ic = ic;
page_attrs.contents = $page.html();
page_attrs.ref = $page; // reference to the current page node in the xml declaration
pages.push(page_attrs);
$new_page.children().each(function() {
self.process($(this));
});
});
var $new_notebook = this.render_element('FormRenderingNotebook', { pages : pages });
$notebook.contents().appendTo($new_notebook);
// Render the notebook and replace $notebook with it
var $new_notebook = self.render_element('FormRenderingNotebook', {'pages': pages});
$notebook.before($new_notebook).remove();
self.process($($new_notebook.children()[0]));
//tabs and invisibility handling
$new_notebook.tabs();
_.each(pages, function(page, i) {
if (! page.__ic)
return;
page.__ic.on("change:effective_invisible", null, function() {
if (!page.__ic.get('effective_invisible') && page.autofocus) {
$new_notebook.tabs('select', i);
return;
}
var current = $new_notebook.tabs("option", "selected");
if (! pages[current].__ic || ! pages[current].__ic.get("effective_invisible"))
return;
var first_visible = _.find(_.range(pages.length), function(i2) {
return (! pages[i2].__ic) || (! pages[i2].__ic.get("effective_invisible"));
});
if (first_visible !== undefined) {
$new_notebook.tabs('select', first_visible);
}
});
});
this.handle_common_properties($new_notebook, $notebook);
// Bind the invisibility changers and find the page to display
var pageid_to_display;
_.each(pages, function(page) {
$tab = $new_notebook.find('a[href=#' + page.id + ']').parent();
$content = $new_notebook.find('#' + page.id);
// Case: <page autofocus="autofocus">;
// We attach the autofocus attribute to the node because it can be useful during the
// page execution
self.attach_node_attr($tab, page.ref, 'autofocus');
self.attach_node_attr($content, page.ref, 'autofocus');
if (!pageid_to_display && page.autofocus) {
// If multiple autofocus, keep the first one
pageid_to_display = page.id;
}
// Case: <page attrs="{'invisible': domain}">;
self.handle_common_properties($tab, page.ref, instance.web.form.NotebookInvisibilityChanger);
self.handle_common_properties($content, page.ref, instance.web.form.NotebookInvisibilityChanger);
});
if (!pageid_to_display) {
pageid_to_display = $new_notebook.find('div[role="tabpanel"]:not(.oe_form_invisible):first').attr('id');
}
// Display page. Note: we can't use bootstrap's show function because it is looking for
// document attached DOM, and the form view is only attached when everything is processed
$new_notebook.find('a[href=#' + pageid_to_display + ']').parent().addClass('active');
$new_notebook.find('#' + pageid_to_display).addClass('active in'); // `in` class is required for bootstrap's fade
self.process($new_notebook.children());
self.handle_common_properties($new_notebook, $notebook);
return $new_notebook;
},
process_separator: function($separator) {
@@ -1591,16 +1595,25 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
}
return $new_label;
},
handle_common_properties: function($new_element, $node) {
handle_common_properties: function($new_element, $node, InvisibilityChanger) {
var str_modifiers = $node.attr("modifiers") || "{}";
var modifiers = JSON.parse(str_modifiers);
var ic = null;
if (modifiers.invisible !== undefined)
ic = new instance.web.form.InvisibilityChanger(this.view, this.view, modifiers.invisible, $new_element);
if (modifiers.invisible !== undefined) {
var InvisibilityChangerCls = InvisibilityChanger || instance.web.form.InvisibilityChanger;
ic = new InvisibilityChangerCls(this.view, this.view, modifiers.invisible, $new_element);
}
$new_element.addClass($node.attr("class") || "");
$new_element.attr('style', $node.attr('style'));
return {invisibility_changer: ic,};
},
/**
* Attach a node attribute to an html element to be able to use it after
* the processing of the page.
*/
attach_node_attr: function($new_element, $node, attr) {
$new_element.data('autofocus', $node.attr(attr));
}
});
/**
@@ -1772,6 +1785,39 @@ instance.web.form.InvisibilityChanger = instance.web.Class.extend(instance.web.P
},
});
// Specialization of InvisibilityChanger for the `notebook` form element to handle more
// elegantly its special cases (i.e. activate the closest visible sibling)
instance.web.form.NotebookInvisibilityChanger = instance.web.form.InvisibilityChanger.extend({
_check_visibility: function() {
this._super();
if (this.get("effective_invisible") === true) {
// Switch to invisible
// Remove this element as active and set a visible sibling active (if there is one)
if (this.$el.hasClass('active')) {
this.$el.removeClass('active in');
var visible_siblings = this.$el.siblings(':not(.oe_form_invisible)');
if (visible_siblings.length) {
$(visible_siblings[0]).addClass('active in');
}
}
} else {
// Switch to visible
// If there is no visible active sibling, set this element as active,
// otherwise:
// if that sibling has autofocus, do nothing
// otherwise, remove that sibling as active and set this element as active
var visible_active_sibling = this.$el.siblings(':not(.oe_form_invisible).active');
if (!(visible_active_sibling.length)) {
this.$el.addClass('active in');
} else if (!$(visible_active_sibling[0]).data('autofocus')) {
this.$el.addClass('active in');
$(visible_active_sibling[0]).removeClass('active in');
}
}
},
});
/**
Base class for all fields, custom widgets and buttons to be displayed in the form view.
+7 -6
View File
@@ -888,17 +888,18 @@
</t>
<t t-name="FormRenderingNotebook">
<div class="oe_clear">
<ul t-attf-class="oe_notebook #{classnames}">
<ul t-attf-class="nav nav-tabs #{classnames}" role="tablist">
<li t-foreach="pages" t-as="page" t-att-modifiers="page.modifiers">
<a t-attf-href="##{page.id}" t-att-accesskey="page.accesskey">
<a role="tab" t-attf-href="##{page.id}" t-att-accesskey="page.accesskey" data-toggle="tab">
<t t-esc="page.string"/>
</a>
</li>
</ul>
</div>
</t>
<t t-name="FormRenderingNotebookPage">
<div t-attf-class="oe_notebook_page #{classnames}" t-att-id="id">
<div class="tab-content">
<div role="tabpanel" t-foreach="pages" t-as="page" t-attf-class="tab-pane fade #{classnames}" t-att-id="id">
<t t-raw="page.contents"/>
</div>
</div>
</div>
</t>
<t t-name="FormRenderingSeparator">