diff --git a/addons/web/static/src/js/framework/utils.js b/addons/web/static/src/js/framework/utils.js index 50a35fbbbea..f99d99ccc14 100644 --- a/addons/web/static/src/js/framework/utils.js +++ b/addons/web/static/src/js/framework/utils.js @@ -341,6 +341,17 @@ function float_is_zero (value, decimals) { return Math.abs(round_precision(value, epsilon)) < epsilon; }; +/** + * Confines a value inside an interval + * @param {Number} [val] the value to confine + * @param {Number} [min] the minimum of the interval + * @param {Number} [max] the maximum of the interval + * @return val if val is in [min, max], min if val < min and max otherwise + */ +function confine (val, min, max) { + return Math.max(min, Math.min(max, val)); +} + function assert (bool) { if (!bool) { throw new Error("AssertionError"); @@ -413,6 +424,7 @@ return { round_precision: round_precision, round_decimals: round_decimals, float_is_zero: float_is_zero, + confine: confine, assert: assert, xor: xor, DropMisordered: DropMisordered, diff --git a/addons/web/static/src/js/widgets/pager.js b/addons/web/static/src/js/widgets/pager.js index c195aaf530d..ee576e923a4 100644 --- a/addons/web/static/src/js/widgets/pager.js +++ b/addons/web/static/src/js/widgets/pager.js @@ -1,6 +1,7 @@ odoo.define('web.Pager', function (require) { "use strict"; +var utils = require('web.utils'); var Widget = require('web.Widget'); var direction = { @@ -12,13 +13,22 @@ var Pager = Widget.extend({ template: "Pager", events: { - 'click .o-pager-previous': 'previous', - 'click .o-pager-next': 'next', + 'click .o_pager_previous': 'previous', + 'click .o_pager_next': 'next', + 'click .o_pager_value': '_edit', }, - // pager goes from 1 to size (included). - // current value is current_min (if limit === 1) - // or the interval [current_min, current_min + limit[ if limit > 1 + /** + * The pager goes from 1 to size (included). + * The current value is current_min if limit === 1 + * or the interval [current_min, current_min + limit[ if limit > 1 + * @param {Widget} [parent] the parent widget + * @param {int} [size] the total number of elements + * @param {int} [current_min] the first element of the current_page + * @param {int} [limit] the number of elements per page + * @param {boolean} [options.can_edit] editable feature of the pager + * @param {boolean} [options.single_page_hidden] (not) to display the pager if only one page + */ init: function (parent, size, current_min, limit, options) { this.state = { size: size, @@ -30,39 +40,142 @@ var Pager = Widget.extend({ return Math.min(this.current_min + this.limit - 1, this.size); } }); - this.options = options || {}; + this.options = _.defaults({}, options, { + can_edit: true, // editable + single_page_hidden: false, // displayed even if there is a single page + }); this._super(parent); }, + /** + * Renders the pager + * @returns {jQuery.Deferred} + */ start: function () { - this.$content = this.$('.o-pager-value'); + this.$value = this.$('.o_pager_value'); + this.$limit = this.$('.o_pager_limit'); this._render(); return this._super(); }, - set_state: function (options) { - _.extend(this.state, options); + /** + * Sets the state of the pager and renders it + * @param {Object} [state] the values to update (size, current_min and limit) + */ + update_state: function (state) { + _.extend(this.state, state); this._render(); }, - previous: function(e) { + /** + * Executes the previous action on the pager + */ + previous: function() { this._change_selection(direction.previous); }, + /** + * Executes the next action on the pager + */ next: function() { this._change_selection(direction.next); }, + /** + * Disables the pager's arrows + */ disable: function() { this.$('button').prop("disabled", true); }, + /** + * Enables the pager's arrows + */ enable: function() { this.$('button').prop("disabled", false); }, + /** + * Private function that displays an input to edit the pager's state + */ + _edit: function() { + if (this.options.can_edit) { + var self = this; + var $input = $('').val(this.$value.html()); + + this.$value.html($input); + $input.focus(); + + // Event handlers + $input.click(function(ev) { + ev.stopPropagation(); // ignore clicks on the input + }); + $input.blur(function(ev) { + self._save($(ev.target)); // save the state when leaving the input + }); + $input.on('keydown', function (ev) { + ev.stopPropagation(); + if (ev.which === $.ui.keyCode.ENTER) { + self._save($(ev.target)); // save on enter + } else if (ev.which === $.ui.keyCode.ESCAPE) { + self._render(); // leave on escape + } + }); + } + }, + + /** + * Private function that saves the state from the content of the input + * @param {jQuery} [$input] the jQuery element containing the new state + */ + _save: function($input) { + var value = $input.val().split("-"); + var min = utils.confine(parseInt(value[0], 10), 1, this.state.size); + var max = utils.confine(parseInt(value[1], 10), 1, this.state.size); + + if (!isNaN(min)) { + this.state.current_min = min; + if (!isNaN(max)) { + this.state.limit = utils.confine(max-min+1, 1, this.state.size); + } else { + // The state has been given as a single value -> set the limit to 1 + this.state.limit = 1; + } + this.trigger('pager_changed', _.clone(this.state)); + } + + // Render the pager's new state (removes the input) + this._render(); + }, + + /** + * Private function that updates the pager's state according to a pager action + * @param {int} [direction] the action (previous or next) on the pager + */ + _change_selection: function (direction) { + var size = this.state.size; + var current_min = this.state.current_min; + var limit = this.state.limit; + + // Compute the new current_min + current_min = (current_min + limit*direction); + if (current_min > size) { + current_min = 1; + } else if ((current_min < 1) && (limit === 1)) { + current_min = size; + } else if ((current_min < 1) && (limit > 1)) { + current_min = size - ((size % limit) || limit) + 1; + } + + this.state.current_min = current_min; + this.trigger('pager_changed', _.clone(this.state)); + this._render(); + }, + + /** + * Private function that renders the pager's state + */ _render: function () { - var state; var size = this.state.size; var current_min = this.state.current_min; var current_max = this.state.current_max; @@ -79,33 +192,16 @@ var Pager = Widget.extend({ this.enable(); } - if (this.state.limit === 1) { - state = "" + current_min + " / " + size; - } else { - state = "" + current_min + "-" + current_max + " / " + size; + var value = "" + current_min; + if (this.state.limit > 1) { + value += "-" + current_max; } - this.$content.html(state); + this.$value.html(value); + this.$limit.html(size); } }, - - _change_selection: function (direction) { - var size = this.state.size; - var current_min = this.state.current_min; - var limit = this.state.limit; - current_min = (current_min + limit*direction); - if (current_min > size) { - current_min = 1; - } else if ((current_min < 1) && (limit === 1)) { - current_min = size; - } else if ((current_min < 1) && (limit > 1)) { - current_min = size - ((size % limit) || limit) + 1; - } - this.state.current_min = current_min; - this.trigger('pager_changed', _.clone(this.state)); - this._render(); - }, }); return Pager; -}); \ No newline at end of file +}); diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index a4b669e9fe8..65b835e63f7 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -1907,10 +1907,10 @@