mirror of
https://github.com/OCA/web.git
synced 2025-02-22 13:21:25 +02:00
Previous this commit the widget only works with 'dots', with this commit works using odoo parse method that support other separators.
261 lines
8.3 KiB
JavaScript
261 lines
8.3 KiB
JavaScript
/* Copyright 2019 GRAP - Quentin DUPONT
|
|
* Copyright 2020 Tecnativa - Alexandre Díaz
|
|
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) */
|
|
|
|
odoo.define('web_widget_numeric_step.field', function (require) {
|
|
"use strict";
|
|
|
|
var field_utils = require('web.field_utils');
|
|
var Registry = require('web.field_registry');
|
|
var FieldFloat = require('web.basic_fields').FieldFloat;
|
|
|
|
|
|
var NumericStep = FieldFloat.extend({
|
|
template: 'web_widget_numeric_step',
|
|
className: 'o_field_numeric_step o_field_number',
|
|
events: _.extend({}, _.omit(FieldFloat.prototype.events, ['change', 'input']), {
|
|
'mousedown .btn_numeric_step': '_onStepMouseDown',
|
|
'touchstart .btn_numeric_step': '_onStepMouseDown',
|
|
'click .btn_numeric_step': '_onStepClick',
|
|
'wheel .input_numeric_step': '_onWheel',
|
|
'keydown .input_numeric_step': '_onKeyDown',
|
|
'change .input_numeric_step': '_onChange',
|
|
'input .input_numeric_step': '_onInput',
|
|
}),
|
|
supportedFieldTypes: ['float', 'integer'],
|
|
|
|
// Values in milliseconds used for mouse down smooth speed feature
|
|
DEF_CLICK_DELAY: 400,
|
|
MIN_DELAY: 50,
|
|
SUBSTRACT_DELAY_STEP: 25,
|
|
|
|
DELAY_THROTTLE_CHANGE: 200,
|
|
|
|
|
|
init: function () {
|
|
this._super.apply(this, arguments);
|
|
|
|
// Widget config
|
|
var max_val = this.nodeOptions.max;
|
|
var min_val = this.nodeOptions.min;
|
|
if (!_.isUndefined(min_val) && !_.isUndefined(max_val) && min_val > max_val) {
|
|
min_val = this.nodeOptions.max;
|
|
max_val = this.nodeOptions.min;
|
|
}
|
|
|
|
this._config = {
|
|
'step': Number(this.nodeOptions.step) || 1,
|
|
'min': Number(min_val),
|
|
'max': Number(max_val),
|
|
};
|
|
|
|
var self = this;
|
|
this._lazyOnChangeTrigger = _.debounce(function() {
|
|
self.$input.trigger("change");
|
|
}, this.DELAY_THROTTLE_CHANGE);
|
|
},
|
|
|
|
/**
|
|
* Add global events listeners
|
|
*
|
|
* @override
|
|
*/
|
|
start: function () {
|
|
var self = this;
|
|
this._click_delay = this.DEF_CLICK_DELAY;
|
|
this._autoStep = false;
|
|
return this._super.apply(this, arguments).then(function () {
|
|
document.addEventListener(
|
|
'mouseup', $.proxy(self, "_onMouseUp"), false);
|
|
document.addEventListener(
|
|
'touchend', $.proxy(self, "_onMouseUp"), false);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Transform database value to usable widget value
|
|
*
|
|
* @override
|
|
*/
|
|
_formatValue: function (value) {
|
|
if (this.mode === 'edit') {
|
|
return this._sanitizeNumberValue(value);
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
|
|
/**
|
|
* Transform widget value to usable database value
|
|
*
|
|
* @override
|
|
*/
|
|
_parseValue: function () {
|
|
var parsedVal = this._super.apply(this, arguments);
|
|
if (this.mode === 'edit') {
|
|
return Number(parsedVal) || 0;
|
|
}
|
|
return parsedVal;
|
|
},
|
|
|
|
/**
|
|
* Adds HTML attributes to the input
|
|
*
|
|
* @override
|
|
*/
|
|
_prepareInput: function () {
|
|
var result = this._super.apply(this, arguments);
|
|
this.$input.attr(_.pick(this.nodeOptions, ['placeholder']));
|
|
// InputField hard set the input type to 'text' or 'password',
|
|
// we force it again to be 'tel'.
|
|
// The widget uses 'tel' type because offers a good layout on
|
|
// mobiles and can accept alphanumeric characters.
|
|
// The bad news is that require implement all good 'number' type
|
|
// features like the minus and plus buttons, steps, min and max...
|
|
// Perhaps in a near future this can be improved to have the best of
|
|
// two types without hacky developments.
|
|
this.$input.attr('type', 'tel');
|
|
return result;
|
|
},
|
|
|
|
/**
|
|
* Select the proper widget input
|
|
*
|
|
* @override
|
|
*/
|
|
_renderEdit: function () {
|
|
this._prepareInput(this.$el.find('input.input_numeric_step'));
|
|
},
|
|
|
|
/**
|
|
* Increase/Decrease widget input value
|
|
*
|
|
* @param {String} mode can be "plus" or "minus"
|
|
*/
|
|
_doStep: function (mode) {
|
|
var cval = 0;
|
|
try {
|
|
var field = this.record.fields[this.name];
|
|
cval = field_utils.parse[field.type](this.$input.val())
|
|
} catch {
|
|
cval = this.value;
|
|
mode = false; // Only set the value in this case
|
|
}
|
|
if (mode === 'plus') {
|
|
cval += this._config.step;
|
|
} else if (mode === 'minus') {
|
|
cval -= this._config.step;
|
|
}
|
|
var nval = this._sanitizeNumberValue(cval);
|
|
if (nval !== this.lastSetValue || !mode) {
|
|
this.$input.val(nval);
|
|
// Every time that user update the value we must trigger an
|
|
// onchange method.
|
|
this._lazyOnChangeTrigger();
|
|
}
|
|
},
|
|
|
|
// Handle Events
|
|
_onStepClick: function (ev) {
|
|
if (!this._autoStep) {
|
|
var mode = ev.target.dataset.mode;
|
|
this._doStep(mode);
|
|
}
|
|
this._autoStep = false;
|
|
},
|
|
|
|
_onStepMouseDown: function (ev) {
|
|
this._interval = setTimeout(
|
|
$.proxy(this, "_whileMouseDown", ev), this._click_delay);
|
|
},
|
|
|
|
_onMouseUp: function () {
|
|
clearTimeout(this._interval);
|
|
this._interval = false;
|
|
this._click_delay = this.DEF_CLICK_DELAY;
|
|
},
|
|
|
|
_whileMouseDown: function (ev) {
|
|
this._autoStep = true;
|
|
var mode = ev.target.dataset.mode;
|
|
this._doStep(mode);
|
|
if (this._click_delay > this.MIN_DELAY) {
|
|
this._click_delay -= this.SUBSTRACT_DELAY_STEP;
|
|
}
|
|
|
|
this._onStepMouseDown(ev);
|
|
},
|
|
|
|
/**
|
|
* Enable mouse wheel support
|
|
*
|
|
* @param {WheelEvent} ev
|
|
*/
|
|
_onWheel: function (ev) {
|
|
ev.preventDefault();
|
|
if (ev.originalEvent.deltaY > 0) {
|
|
this._doStep('minus');
|
|
} else {
|
|
this._doStep('plus');
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Enable keyboard arrows support
|
|
*
|
|
* @param {KeyEvent} ev
|
|
*/
|
|
_onKeyDown: function (ev) {
|
|
if (ev.keyCode === $.ui.keyCode.UP) {
|
|
this._doStep('plus');
|
|
} else if (ev.keyCode === $.ui.keyCode.DOWN) {
|
|
this._doStep('minus');
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Sanitize user input value
|
|
*
|
|
* @override
|
|
*/
|
|
_onChange: function (ev) {
|
|
ev.target.value = this._sanitizeNumberValue(ev.target.value);
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
|
|
// Helper Functions
|
|
/**
|
|
* Check limits and precision of the value.
|
|
* If the value 'is not a number', the function does nothing to
|
|
* be good with other possible modules.
|
|
*
|
|
* @param {Number} value
|
|
* @returns {Number}
|
|
*/
|
|
_sanitizeNumberValue: function (value) {
|
|
var cval = Number(value);
|
|
if (_.isNaN(cval)) {
|
|
return value;
|
|
}
|
|
if (!_.isNaN(this._config.min) && cval < this._config.min) {
|
|
cval = this._config.min;
|
|
} else if (!_.isNaN(this._config.max) && cval > this._config.max) {
|
|
cval = this._config.max;
|
|
}
|
|
|
|
var field = this.record.fields[this.name];
|
|
var formattedValue = field_utils.format[field.type](cval, field, {
|
|
data: this.record.data,
|
|
escape: true,
|
|
isPassword: false,
|
|
digits: field.digits,
|
|
});
|
|
return formattedValue;
|
|
},
|
|
});
|
|
|
|
Registry.add('numeric_step', NumericStep);
|
|
|
|
return NumericStep;
|
|
|
|
});
|