280 lines
10 KiB
JavaScript
280 lines
10 KiB
JavaScript
flectra.define('web.ControlPanelMixin', function (require) {
|
|
"use strict";
|
|
|
|
/**
|
|
* Mixin allowing widgets to communicate with the ControlPanel. Widgets needing a
|
|
* ControlPanel should use this mixin and call update_control_panel(cp_status) where
|
|
* cp_status contains information for the ControlPanel to update itself.
|
|
*
|
|
* Note that the API is slightly awkward. Hopefully we will improve this when
|
|
* we get the time to refactor the control panel.
|
|
*
|
|
* For example, here is what a typical client action would need to do to add
|
|
* support for a control panel with some buttons::
|
|
*
|
|
* var ControlPanelMixin = require('web.ControlPanelMixin');
|
|
*
|
|
* var SomeClientAction = Widget.extend(ControlPanelMixin, {
|
|
* ...
|
|
* start: function () {
|
|
* this._renderButtons();
|
|
* this._updateControlPanel();
|
|
* ...
|
|
* },
|
|
* do_show: function () {
|
|
* ...
|
|
* this._updateControlPanel();
|
|
* },
|
|
* _renderButtons: function () {
|
|
* this.$buttons = $(QWeb.render('SomeTemplate.Buttons'));
|
|
* this.$buttons.on('click', ...);
|
|
* },
|
|
* _updateControlPanel: function () {
|
|
* this.update_control_panel({
|
|
* breadcrumbs: this.action_manager.get_breadcrumbs(),
|
|
* cp_content: {
|
|
* $buttons: this.$buttons,
|
|
* },
|
|
* });
|
|
*/
|
|
var ControlPanelMixin = {
|
|
need_control_panel: true,
|
|
/**
|
|
* @param {web.Bus} [cp_bus] Bus to communicate with the ControlPanel
|
|
*/
|
|
set_cp_bus: function(cp_bus) {
|
|
this.cp_bus = cp_bus;
|
|
},
|
|
/**
|
|
* Triggers 'update' on the cp_bus to update the ControlPanel according to cp_status
|
|
* @param {Object} [cp_status] see web.ControlPanel.update() for a description
|
|
* @param {Object} [options] see web.ControlPanel.update() for a description
|
|
*/
|
|
update_control_panel: function(cp_status, options) {
|
|
this.cp_bus.trigger("update", cp_status || {}, options || {});
|
|
},
|
|
};
|
|
|
|
return ControlPanelMixin;
|
|
|
|
});
|
|
|
|
flectra.define('web.ControlPanel', function (require) {
|
|
"use strict";
|
|
|
|
var Bus = require('web.Bus');
|
|
var data = require('web.data');
|
|
var Widget = require('web.Widget');
|
|
|
|
var ControlPanel = Widget.extend({
|
|
template: 'ControlPanel',
|
|
/**
|
|
* @param {String} [template] the QWeb template to render the ControlPanel.
|
|
* By default, the template 'ControlPanel' will be used
|
|
*/
|
|
init: function(parent, template) {
|
|
this._super(parent);
|
|
if (template) {
|
|
this.template = template;
|
|
}
|
|
|
|
this.bus = new Bus();
|
|
this.bus.on("update", this, this.update);
|
|
},
|
|
/**
|
|
* Renders the control panel and creates a dictionnary of its exposed elements
|
|
* @return {jQuery.Deferred}
|
|
*/
|
|
start: function() {
|
|
// Exposed jQuery nodesets
|
|
this.nodes = {
|
|
$breadcrumbs: this.$('.breadcrumb'),
|
|
$buttons: this.$('.o_cp_buttons'),
|
|
$pager: this.$('.o_cp_pager'),
|
|
$searchview: this.$('.o_cp_searchview'),
|
|
$searchview_buttons: this.$('.o_search_options'),
|
|
$sidebar: this.$('.o_cp_sidebar'),
|
|
$switch_buttons: this.$('.o_cp_switch_buttons'),
|
|
};
|
|
|
|
// Prevent the search dropdowns to close when clicking inside them
|
|
this.$el.on('click.bs.dropdown', '.o_search_options .dropdown-menu', function (e) {
|
|
e.stopPropagation();
|
|
});
|
|
|
|
// By default, hide the ControlPanel and remove its contents from the DOM
|
|
this._toggle_visibility(false);
|
|
|
|
return this._super();
|
|
},
|
|
destroy: function() {
|
|
this._clear_breadcrumbs_handlers();
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
/**
|
|
* @return {Object} the Bus the ControlPanel is listening on
|
|
*/
|
|
get_bus: function() {
|
|
return this.bus;
|
|
},
|
|
/**
|
|
* Updates the content and displays the ControlPanel
|
|
* @param {Object} [status.active_view] the current active view
|
|
* @param {Array} [status.breadcrumbs] the breadcrumbs to display (see _render_breadcrumbs() for
|
|
* precise description)
|
|
* @param {Object} [status.cp_content] dictionnary containing the new ControlPanel jQuery elements
|
|
* @param {Boolean} [status.hidden] true if the ControlPanel should be hidden
|
|
* @param {openerp.web.SearchView} [status.searchview] the searchview widget
|
|
* @param {Boolean} [status.search_view_hidden] true if the searchview is hidden, false otherwise
|
|
* @param {Boolean} [options.clear] set to true to clear from control panel
|
|
* elements that are not in status.cp_content
|
|
*/
|
|
update: function(status, options) {
|
|
this._toggle_visibility(!status.hidden);
|
|
|
|
// Don't update the ControlPanel in headless mode as the views have
|
|
// inserted themselves the buttons where they want, so inserting them
|
|
// again in the ControlPanel will remove them from where they should be
|
|
if (!status.hidden) {
|
|
options = _.defaults({}, options, {
|
|
clear: true, // clear control panel by default
|
|
});
|
|
var new_cp_content = status.cp_content || {};
|
|
|
|
// Render the breadcrumbs
|
|
if (status.breadcrumbs) {
|
|
this._clear_breadcrumbs_handlers();
|
|
this.$breadcrumbs = this._render_breadcrumbs(status.breadcrumbs);
|
|
new_cp_content.$breadcrumbs = this.$breadcrumbs;
|
|
}
|
|
|
|
// Detach control_panel old content and attach new elements
|
|
if (options.clear) {
|
|
this._detach_content(this.nodes);
|
|
// Show the searchview buttons area, which might have been hidden by
|
|
// the searchview, as client actions may insert elements into it
|
|
this.nodes.$searchview_buttons.show();
|
|
} else {
|
|
this._detach_content(_.pick(this.nodes, _.keys(new_cp_content)));
|
|
}
|
|
this._attach_content(new_cp_content);
|
|
|
|
// Update the searchview and switch buttons
|
|
this._update_search_view(status.searchview, status.search_view_hidden);
|
|
if (status.active_view_selector) {
|
|
this._update_switch_buttons(status.active_view_selector);
|
|
}
|
|
}
|
|
},
|
|
/**
|
|
* Private function that hides (or shows) the ControlPanel in headless (resp. non-headless) mode
|
|
* Also detaches or attaches its contents to clean the DOM
|
|
* @param {Boolean} [visible] true to show the control panel, false to hide it
|
|
*/
|
|
_toggle_visibility: function(visible) {
|
|
this.do_toggle(visible);
|
|
if (!visible && !this.$content) {
|
|
this.$content = this.$el.contents().detach();
|
|
} else if (this.$content) {
|
|
this.$content.appendTo(this.$el);
|
|
this.$content = null;
|
|
}
|
|
},
|
|
/**
|
|
* Private function that detaches the content of the ControlPanel
|
|
* @param {Object} [elements_to_detach] subset of this.nodes to detach
|
|
*/
|
|
_detach_content: function(elements_to_detach) {
|
|
_.each(elements_to_detach, function($nodeset) {
|
|
$nodeset.contents().detach();
|
|
});
|
|
},
|
|
/**
|
|
* Private function that attaches content to the ControlPanel
|
|
* @param {Object} [content] dictionnary of jQuery elements to attach, whose keys
|
|
* are jQuery nodes identifiers in this.nodes
|
|
*/
|
|
_attach_content: function(content) {
|
|
var self = this;
|
|
_.each(content, function($nodeset, $element) {
|
|
if ($nodeset && self.nodes[$element]) {
|
|
self.nodes[$element].append($nodeset);
|
|
}
|
|
});
|
|
},
|
|
/**
|
|
* Private function that removes active class on all switch-buttons and adds
|
|
* it to the one of the active view
|
|
* @param {Object} [active_view_selector] the selector of the div to activate
|
|
*/
|
|
_update_switch_buttons: function(active_view_selector) {
|
|
_.each(this.nodes.$switch_buttons.find('button'), function(button) {
|
|
$(button).removeClass('active');
|
|
});
|
|
this.$(active_view_selector).addClass('active');
|
|
},
|
|
/**
|
|
* Private function that renders the breadcrumbs
|
|
* @param {Array} [breadcrumbs] list of objects containing the following keys:
|
|
* - action: the action to execute when clicking on this part of the breadcrumbs
|
|
* - index: the index in the breadcrumbs (starting at 0)
|
|
* - title: what to display in the breadcrumbs
|
|
* @return {Array} list of breadcrumbs' li jQuery elements
|
|
*/
|
|
_render_breadcrumbs: function (breadcrumbs) {
|
|
var self = this;
|
|
return breadcrumbs.map(function (bc, index) {
|
|
return self._render_breadcrumbs_li(bc, index, breadcrumbs.length);
|
|
});
|
|
},
|
|
/**
|
|
* Private function that renders a breadcrumbs' li Jquery element
|
|
*/
|
|
_render_breadcrumbs_li: function (bc, index, length) {
|
|
var self = this;
|
|
var is_last = (index === length-1);
|
|
var li_content = bc.title && _.escape(bc.title.trim()) || data.noDisplayContent;
|
|
var $bc = $('<li>')
|
|
.append(is_last ? li_content : $('<a>').html(li_content))
|
|
.toggleClass('active', is_last);
|
|
if (!is_last) {
|
|
$bc.click(function () {
|
|
self.trigger("on_breadcrumb_click", bc.action, bc.index);
|
|
});
|
|
}
|
|
return $bc;
|
|
},
|
|
/**
|
|
* Private function that removes event handlers attached on the currently
|
|
* displayed breadcrumbs.
|
|
*/
|
|
_clear_breadcrumbs_handlers: function () {
|
|
if (this.$breadcrumbs) {
|
|
_.each(this.$breadcrumbs, function ($bc) {
|
|
$bc.off();
|
|
});
|
|
}
|
|
},
|
|
/**
|
|
* Private function that updates the SearchView's visibility and extend the
|
|
* breadcrumbs area if the SearchView is not visible
|
|
* @param {openerp.web.SearchView} [searchview] the searchview Widget
|
|
* @param {Boolean} [is_hidden] visibility of the searchview
|
|
*/
|
|
_update_search_view: function(searchview, is_hidden) {
|
|
if (searchview) {
|
|
// Set the $buttons div (in the DOM) of the searchview as the $buttons
|
|
// have been appended to a jQuery node not in the DOM at SearchView initialization
|
|
searchview.$buttons = this.nodes.$searchview_buttons;
|
|
searchview.toggle_visibility(!is_hidden);
|
|
}
|
|
|
|
this.nodes.$searchview.toggle(!is_hidden);
|
|
this.$el.toggleClass('o_breadcrumb_full', !!is_hidden);
|
|
},
|
|
});
|
|
|
|
return ControlPanel;
|
|
|
|
});
|