[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: -webkit-radial-gradient(circle, #eee 0%, #ccc 40%, #bbb 100%);
background-image: -moz-radial-gradient(#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: -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 { .openerp .oe_kanban_status_green {
background: green; background: green;
@@ -616,79 +616,6 @@
font-style: italic; font-style: italic;
text-decoration: none; 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 { .openerp .oe_dropdown, .openerp .oe_dropdown_hover, .openerp .oe_dropdown_toggle {
position: relative; position: relative;
cursor: pointer; cursor: pointer;
-56
View File
@@ -555,62 +555,6 @@ $sheet-padding: 16px
text-decoration: none text-decoration: none
margin-bottom: 1px 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 {{{ // Dropdown {{{
.oe_dropdown, .oe_dropdown_hover, .oe_dropdown_toggle .oe_dropdown, .oe_dropdown_hover, .oe_dropdown_toggle
position: relative position: relative
+84 -38
View File
@@ -1514,50 +1514,54 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
}, },
process_notebook: function($notebook) { process_notebook: function($notebook) {
var self = this; var self = this;
// Extract useful info from the notebook xml declaration
var pages = []; var pages = [];
$notebook.find('> page').each(function() { $notebook.find('> page').each(function() {
var $page = $(this); var $page = $(this);
var page_attrs = $page.getAttributes(); var page_attrs = $page.getAttributes();
page_attrs.id = _.uniqueId('notebook_page_'); page_attrs.id = _.uniqueId('notebook_page_');
var $new_page = self.render_element('FormRenderingNotebookPage', page_attrs); page_attrs.contents = $page.html();
$page.contents().appendTo($new_page); page_attrs.ref = $page; // reference to the current page node in the xml declaration
$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;
pages.push(page_attrs); 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(); $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; return $new_notebook;
}, },
process_separator: function($separator) { process_separator: function($separator) {
@@ -1591,16 +1595,25 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
} }
return $new_label; 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 str_modifiers = $node.attr("modifiers") || "{}";
var modifiers = JSON.parse(str_modifiers); var modifiers = JSON.parse(str_modifiers);
var ic = null; var ic = null;
if (modifiers.invisible !== undefined) if (modifiers.invisible !== undefined) {
ic = new instance.web.form.InvisibilityChanger(this.view, this.view, modifiers.invisible, $new_element); 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.addClass($node.attr("class") || "");
$new_element.attr('style', $node.attr('style')); $new_element.attr('style', $node.attr('style'));
return {invisibility_changer: ic,}; 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. 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 t-name="FormRenderingNotebook"> <t t-name="FormRenderingNotebook">
<div class="oe_clear"> <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"> <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"/> <t t-esc="page.string"/>
</a> </a>
</li> </li>
</ul> </ul>
</div> <div class="tab-content">
</t> <div role="tabpanel" t-foreach="pages" t-as="page" t-attf-class="tab-pane fade #{classnames}" t-att-id="id">
<t t-name="FormRenderingNotebookPage"> <t t-raw="page.contents"/>
<div t-attf-class="oe_notebook_page #{classnames}" t-att-id="id"> </div>
</div>
</div> </div>
</t> </t>
<t t-name="FormRenderingSeparator"> <t t-name="FormRenderingSeparator">