3
0
Fork 0

[MIG] web_widget_numeric_step: Migration to 16.0

16.0
dsolanki 2023-03-21 10:59:54 +05:30
parent 976797cfa0
commit 2b72477e08
12 changed files with 136 additions and 408 deletions

View File

@ -14,7 +14,7 @@ Web Widget Numeric Step
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3 :alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github
:target: https://github.com/OCA/web/tree/15.0/web_widget_numeric_step :target: https://github.com/OCA/web/tree/16.0/web_widget_numeric_step
:alt: OCA/web :alt: OCA/web
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/web-15-0/web-15-0-web_widget_numeric_step :target: https://translation.odoo-community.org/projects/web-15-0/web-15-0-web_widget_numeric_step
@ -60,7 +60,6 @@ Example for an 0.25 step, min to -1 and max to 10 :
- step > Amount to increase/decrease (default: 1.0) - step > Amount to increase/decrease (default: 1.0)
- min > Min. value allowed (default: no limit) - min > Min. value allowed (default: no limit)
- max > Max. value allowed (default: no limit) - max > Max. value allowed (default: no limit)
- auto_select > Select the content when the element get focus (default: False)
- placeholder > Define the placeholder text (default: None) - placeholder > Define the placeholder text (default: None)
**Examples** **Examples**
@ -108,6 +107,7 @@ Contributors
* Helly kapatel <helly.kapatel@initos.com> * Helly kapatel <helly.kapatel@initos.com>
* Thanakrit Pintana <thanakrit.p39@gmail.com> * Thanakrit Pintana <thanakrit.p39@gmail.com>
* Dhara Solanki <dhara.solanki@initos.com>
Maintainers Maintainers
~~~~~~~~~~~ ~~~~~~~~~~~
@ -122,6 +122,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and mission is to support the collaborative development of Odoo features and
promote its widespread use. promote its widespread use.
This module is part of the `OCA/web <https://github.com/OCA/web/tree/15.0/web_widget_numeric_step>`_ project on GitHub. This module is part of the `OCA/web <https://github.com/OCA/web/tree/16.0/web_widget_numeric_step>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@ -10,14 +10,10 @@
"license": "AGPL-3", "license": "AGPL-3",
"website": "https://github.com/OCA/web", "website": "https://github.com/OCA/web",
"depends": ["web"], "depends": ["web"],
"demo": ["demo/res_users_view.xml"],
"assets": { "assets": {
"web.assets_backend": [ "web.assets_backend": [
"web_widget_numeric_step/static/src/js/numeric_step.js",
"web_widget_numeric_step/static/src/css/numeric_step.scss",
],
"web.assets_qweb": [
"web_widget_numeric_step/static/src/xml/numeric_step.xml", "web_widget_numeric_step/static/src/xml/numeric_step.xml",
"web_widget_numeric_step/static/src/js/numeric_step.esm.js",
], ],
}, },
"auto_install": False, "auto_install": False,

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!-- Copyright 2020 Tecnativa - Alexandre Díaz
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo>
<record id="view_users_form" model="ir.ui.view">
<field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_form" />
<field name="arch" type="xml">
<xpath expr="//div[hasclass('oe_title')]" position="after">
<group>
<field
name="credit_limit"
widget="numeric_step"
options="{'step': 3, 'min': -10, 'max': 130}"
/>
</group>
</xpath>
</field>
</record>
</odoo>

View File

@ -9,3 +9,4 @@
* Helly kapatel <helly.kapatel@initos.com> * Helly kapatel <helly.kapatel@initos.com>
* Thanakrit Pintana <thanakrit.p39@gmail.com> * Thanakrit Pintana <thanakrit.p39@gmail.com>
* Dhara Solanki <dhara.solanki@initos.com>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -367,7 +367,7 @@ ul.auto-toc {
!! This file is generated by oca-gen-addon-readme !! !! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !! !! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/web/tree/15.0/web_widget_numeric_step"><img alt="OCA/web" src="https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/web-15-0/web-15-0-web_widget_numeric_step"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/162/15.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p> <p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/web/tree/16.0/web_widget_numeric_step"><img alt="OCA/web" src="https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/web-15-0/web-15-0-web_widget_numeric_step"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/162/15.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>This widget changes input number field and make it easier to increment the number thanks to 2 buttons (+ and -). <p>This widget changes input number field and make it easier to increment the number thanks to 2 buttons (+ and -).
Use JS native logic for input number, so you can use the options <tt class="docutils literal">min</tt>, <tt class="docutils literal">max</tt>, <tt class="docutils literal">step</tt>, <tt class="docutils literal">placeholder</tt>.</p> Use JS native logic for input number, so you can use the options <tt class="docutils literal">min</tt>, <tt class="docutils literal">max</tt>, <tt class="docutils literal">step</tt>, <tt class="docutils literal">placeholder</tt>.</p>
<p>Demo available at <cite>Settings &gt; Users &amp; Companies &gt; Users &gt; *Select One* &gt; See Credit Limit field</cite></p> <p>Demo available at <cite>Settings &gt; Users &amp; Companies &gt; Users &gt; *Select One* &gt; See Credit Limit field</cite></p>
@ -457,7 +457,7 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose <p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and mission is to support the collaborative development of Odoo features and
promote its widespread use.</p> promote its widespread use.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/web/tree/15.0/web_widget_numeric_step">OCA/web</a> project on GitHub.</p> <p>This module is part of the <a class="reference external" href="https://github.com/OCA/web/tree/16.0/web_widget_numeric_step">OCA/web</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p> <p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div> </div>
</div> </div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 108 KiB

View File

@ -1,11 +0,0 @@
.widget_numeric_step {
display: inline-flex;
}
.numeric_step_editing_cell {
min-width: 120px;
.input_numeric_step {
height: auto;
}
}

View File

@ -0,0 +1,92 @@
/** @odoo-module */
import {registry} from "@web/core/registry";
import {standardFieldProps} from "@web/views/fields/standard_field_props";
import {_lt} from "@web/core/l10n/translation";
import {FloatField} from "@web/views/fields/float/float_field";
export class NumericStep extends FloatField {
setup() {
super.setup();
}
_onFocusInput(ev) {
const $el = $(ev.target).parent().find(".widget_numeric_step_btn");
$el.removeClass("d-none");
}
_onFocusOutInput(ev) {
const $el = $(ev.target).find(".widget_numeric_step_btn");
$el.addClass("d-none");
}
_onStepClick(ev) {
const $el = $(ev.target).parent().parent().find("input");
$el.focus();
const mode = $(ev.target).data("mode");
this._doStep(mode);
}
_onKeyDown(ev) {
if (ev.keyCode === $.ui.keyCode.UP) {
this._doStep("plus");
} else if (ev.keyCode === $.ui.keyCode.DOWN) {
this._doStep("minus");
}
}
_onWheel(ev) {
ev.preventDefault();
if (ev.deltaY > 0) {
this._doStep("minus");
} else {
this._doStep("plus");
}
}
updateField(val) {
return Promise.resolve(this.props.update(val));
}
_doStep(mode) {
let cval = this.props.value;
if (mode === "plus") {
cval += this.props.step;
} else if (mode === "minus") {
cval -= this.props.step;
}
if (cval < this.props.min) {
cval = this.props.min;
} else if (cval > this.props.max) {
cval = this.props.max;
}
this.updateField(cval);
this.props.setDirty(this._isSetDirty(cval));
this.props.setDirty(false);
}
_isSetDirty(val) {
return this.props.value != val;
}
}
NumericStep.template = "web_widget_numeric_step";
NumericStep.props = {
...standardFieldProps,
name: {type: String, optional: true},
inputType: {type: String, optional: true},
step: {type: Number, optional: true},
min: {type: Number, optional: true},
max: {type: Number, optional: true},
placeholder: {type: String, optional: true},
};
NumericStep.displayName = _lt("Numeric Step");
NumericStep.supportedTypes = ["float"];
NumericStep.defaultProps = {
inputType: "text",
};
NumericStep.extractProps = ({attrs}) => {
return {
name: attrs.name,
inputType: attrs.options.type,
step: attrs.options.step || 1,
min: attrs.options.min,
max: attrs.options.max,
placeholder: attrs.options.placeholder,
};
};
registry.category("fields").add("numeric_step", NumericStep);

View File

@ -1,336 +0,0 @@
/* 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";
const field_utils = require("web.field_utils");
const Registry = require("web.field_registry");
const FieldFloat = require("web.basic_fields").FieldFloat;
const 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",
"focusin .input_numeric_step": "_onFocusIn",
"focusout .widget_numeric_step": "_onFocusOut",
}),
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,
/**
* @override
*/
init: function () {
this._super.apply(this, arguments);
// Widget config
let max_val = this.nodeOptions.max;
let 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),
autoSelect: this.nodeOptions.auto_select,
};
this._lazyOnChangeTrigger = _.debounce(
() => this.$input.trigger("change"),
this.DELAY_THROTTLE_CHANGE
);
this._auto_step_interval = false;
},
/**
* Add global events listeners
*
* @override
*/
start: function () {
this._click_delay = this.DEF_CLICK_DELAY;
this._autoStep = false;
return this._super.apply(this, arguments).then(() => {
document.addEventListener("mouseup", this._onMouseUp.bind(this), false);
document.addEventListener(
"touchend",
this._onMouseUp.bind(this),
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 () {
const 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 () {
const 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 () {
_.defer(() =>
this.$el
.parents("td.o_numeric_step_cell")
.addClass("numeric_step_editing_cell")
);
this._prepareInput(this.$el.find("input.input_numeric_step"));
},
/**
* Resets the content to the formated value in readonly mode.
*
* @override
*/
_renderReadonly: function () {
this.$el
.parents("td.numeric_step_editing_cell")
.removeClass("numeric_step_editing_cell");
this._super.apply(this, arguments);
},
/**
* Increase/Decrease widget input value
*
* @param {String} mode can be "plus" or "minus"
*/
_doStep: function (mode) {
let cval = 0;
try {
const field = this.record.fields[this.name];
cval = field_utils.parse[field.type](this.$input.val());
} catch (e) {
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;
}
const 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();
}
},
/**
* @private
*/
_clearStepInterval: function () {
clearTimeout(this._auto_step_interval);
this._auto_step_interval = false;
this._click_delay = this.DEF_CLICK_DELAY;
},
// Handle Events
/**
* @private
* @param {MouseClickEvent} ev
*/
_onStepClick: function (ev) {
if (!this._autoStep) {
const mode = ev.target.dataset.mode;
this._doStep(mode);
}
this._autoStep = false;
},
/**
* @private
* @param {MouseClickEvent} ev
*/
_onStepMouseDown: function (ev) {
if (ev.button === 0 && !this._auto_step_interval) {
this._auto_step_interval = setTimeout(
this._whileMouseDown.bind(this, ev),
this._click_delay
);
}
},
/**
* Auto select all content when user enters into fields with this
* widget.
*
* @private
*/
_onFocusIn: function () {
if (this._config.autoSelect) {
this.$input.select();
}
},
/**
* @private
* @param {FocusoutEvent} ev
*/
_onFocusOut: function () {
if (this._auto_step_interval) {
this._clearStepInterval();
}
},
/**
* @private
*/
_onMouseUp: function () {
this._clearStepInterval();
},
/**
* @private
* @param {MouseClickEvent} ev
*/
_whileMouseDown: function (ev) {
this._autoStep = true;
const mode = ev.target.dataset.mode;
this._doStep(mode);
if (this._click_delay > this.MIN_DELAY) {
this._click_delay -= this.SUBSTRACT_DELAY_STEP;
}
this._auto_step_interval = false;
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) {
let 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;
}
const field = this.record.fields[this.name];
// Formatted value
return field_utils.format[field.type](cval, field, {
data: this.record.data,
escape: true,
isPassword: false,
digits: field.digits,
});
},
});
Registry.add("numeric_step", NumericStep);
return NumericStep;
});

View File

@ -4,37 +4,43 @@ Copyright 2019 GRAP - Quentin DUPONT
Copyright 2020 Tecnativa - Alexandre Díaz Copyright 2020 Tecnativa - Alexandre Díaz
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
--> -->
<templates> <template>
<t t-name="web_widget_numeric_step"> <t t-name="web_widget_numeric_step" owl="1">
<t t-if="widget.mode != 'readonly'"> <div
<div class="input-group widget_numeric_step"> class="d-flex widget_numeric_step"
<div class="input-group-prepend"> t-on-mouseleave="_onFocusOutInput"
<button t-on-mouseenter="_onFocusInput"
class="fa fa-minus btn btn-default btn_numeric_step" >
aria-label="Minus" <div class="input-group-prepend d-none widget_numeric_step_btn">
title="Minus" <button
type="button" class="fa fa-minus btn btn-default btn_numeric_step"
data-mode="minus" aria-label="Minus"
/> title="Minus"
</div> type="button"
<input data-mode="minus"
type="tel" t-on-click="_onStepClick"
class="form-control input_numeric_step"
aria-label="Value"
/> />
<div class="input-group-append">
<button
class="fa fa-plus btn btn-default btn_numeric_step"
aria-label="Plus"
title="Plus"
type="button"
data-mode="plus"
/>
</div>
</div> </div>
</t> <input
<t t-else=""> t-att-id="props.id"
<field /> t-ref="numpadDecimal"
</t> t-att-placeholder="props.placeholder"
t-att-type="props.inputType"
class="o_input input_numeric_step"
t-att-step="props.step"
t-on-keydown="_onKeyDown"
t-on-wheel="_onWheel"
/>
<div class="input-group-append d-none widget_numeric_step_btn">
<button
class="fa fa-plus btn btn-default btn_numeric_step"
aria-label="Plus"
title="Plus"
type="button"
data-mode="plus"
t-on-click="_onStepClick"
/>
</div>
</div>
</t> </t>
</templates> </template>