diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/web/static/src/js/components/pager.js | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/web/static/src/js/components/pager.js')
| -rw-r--r-- | addons/web/static/src/js/components/pager.js | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/addons/web/static/src/js/components/pager.js b/addons/web/static/src/js/components/pager.js new file mode 100644 index 00000000..2d8d402a --- /dev/null +++ b/addons/web/static/src/js/components/pager.js @@ -0,0 +1,225 @@ +odoo.define('web.Pager', function (require) { + "use strict"; + + const { useAutofocus } = require('web.custom_hooks'); + + const { Component, hooks } = owl; + const { useState } = hooks; + + /** + * Pager + * + * The pager goes from 1 to size (included). + * The current value is currentMinimum if limit === 1 or the interval: + * [currentMinimum, currentMinimum + limit[ if limit > 1]. + * The value can be manually changed by clicking on the pager value and giving + * an input matching the pattern: min[,max] (in which the comma can be a dash + * or a semicolon). + * The pager also provides two buttons to quickly change the current page (next + * or previous). + * @extends Component + */ + class Pager extends Component { + /** + * @param {Object} [props] + * @param {int} [props.size] the total number of elements + * @param {int} [props.currentMinimum] the first element of the current_page + * @param {int} [props.limit] the number of elements per page + * @param {boolean} [props.editable] editable feature of the pager + * @param {function} [props.validate] callback returning a Promise to + * validate changes + * @param {boolean} [props.withAccessKey] can be disabled, for example, + * for x2m widgets + */ + constructor() { + super(...arguments); + + this.state = useState({ + disabled: false, + editing: false, + }); + + useAutofocus(); + } + + async willUpdateProps() { + this.state.editing = false; + this.state.disabled = false; + } + + //--------------------------------------------------------------------- + // Getters + //--------------------------------------------------------------------- + + /** + * @returns {number} + */ + get maximum() { + return Math.min(this.props.currentMinimum + this.props.limit - 1, this.props.size); + } + + /** + * @returns {boolean} true iff there is only one page + */ + get singlePage() { + return (1 === this.props.currentMinimum) && (this.maximum === this.props.size); + } + + /** + * @returns {number} + */ + get value() { + return this.props.currentMinimum + (this.props.limit > 1 ? `-${this.maximum}` : ''); + } + + //--------------------------------------------------------------------- + // Private + //--------------------------------------------------------------------- + + /** + * Update the pager's state according to a pager action + * @private + * @param {number} [direction] the action (previous or next) on the pager + */ + async _changeSelection(direction) { + try { + await this.props.validate(); + } catch (err) { + return; + } + const { limit, size } = this.props; + + // Compute the new currentMinimum + let currentMinimum = (this.props.currentMinimum + limit * direction); + if (currentMinimum > size) { + currentMinimum = 1; + } else if ((currentMinimum < 1) && (limit === 1)) { + currentMinimum = size; + } else if ((currentMinimum < 1) && (limit > 1)) { + currentMinimum = size - ((size % limit) || limit) + 1; + } + + // The re-rendering of the pager must be done before the trigger of + // event 'pager-changed' as the rendering may enable the pager + // (and a common use is to disable the pager when this event is + // triggered, and to re-enable it when the data have been reloaded). + this._updateAndDisable(currentMinimum, limit); + } + + /** + * Save the state from the content of the input + * @private + * @param {string} value the new raw pager value + * @returns {Promise} + */ + async _saveValue(value) { + try { + await this.props.validate(); + } catch (err) { + return; + } + const [min, max] = value.trim().split(/\s*[\-\s,;]\s*/); + + let currentMinimum = Math.max(Math.min(parseInt(min, 10), this.props.size), 1); + let maximum = max ? Math.max(Math.min(parseInt(max, 10), this.props.size), 1) : min; + + if ( + !isNaN(currentMinimum) && + !isNaN(maximum) && + currentMinimum <= maximum + ) { + const limit = Math.max(maximum - currentMinimum) + 1; + this._updateAndDisable(currentMinimum, limit); + } + } + + /** + * Commits the current input value. There are two scenarios: + * - the value is the same: the pager toggles back to readonly + * - the value changed: the pager is disabled to prevent furtherchanges + * Either way the "pager-changed" event is triggered to reload the + * view. + * @private + * @param {number} currentMinimum + * @param {number} limit + */ + _updateAndDisable(currentMinimum, limit) { + if ( + currentMinimum !== this.props.currentMinimum || + limit !== this.props.limit + ) { + this.state.disabled = true; + } else { + // In this case we want to trigger an update, but since it will + // not re-render the pager (current props === next props) we + // have to disable the edition manually here. + this.state.editing = false; + } + this.trigger('pager-changed', { currentMinimum, limit }); + } + + //--------------------------------------------------------------------- + // Handlers + //--------------------------------------------------------------------- + + /** + * @private + */ + _onEdit() { + if ( + this.props.editable && // editable + !this.state.editing && // not already editing + !this.state.disabled // not being changed already + ) { + this.state.editing = true; + } + } + + /** + * @private + * @param {InputEvent} ev + */ + _onValueChange(ev) { + this._saveValue(ev.currentTarget.value); + if (!this.state.disabled) { + ev.preventDefault(); + } + } + + /** + * @private + * @param {KeyboardEvent} ev + */ + _onValueKeydown(ev) { + switch (ev.key) { + case 'Enter': + ev.preventDefault(); + ev.stopPropagation(); + this._saveValue(ev.currentTarget.value); + break; + case 'Escape': + ev.preventDefault(); + ev.stopPropagation(); + this.state.editing = false; + break; + } + } + } + + Pager.defaultProps = { + editable: true, + validate: async () => { }, + withAccessKey: true, + }; + Pager.props = { + currentMinimum: { type: Number, optional: 1 }, + editable: Boolean, + limit: { validate: l => !isNaN(l), optional: 1 }, + size: { type: Number, optional: 1 }, + validate: Function, + withAccessKey: Boolean, + }; + Pager.template = 'web.Pager'; + + return Pager; +}); |
