summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2018-11-29 20:06:16 +0100
committerChristophe Simonis <chs@odoo.com>2018-11-29 20:06:16 +0100
commitce4cc24621bf6430d7d3a2d469b375b59120c200 (patch)
tree3837b5fe41ff88b24136ab53c871bc01f965cab1 /addons/web/static/src/js
parent4d7aa97da38e497fef31a06543bfa5d2c8785708 (diff)
parent82a1e1dcc33d7c123e50d3e1aacb5e127f57c521 (diff)
[MERGE] forward port branch 12.0 up to 82a1e1dcc33
Diffstat (limited to 'addons/web/static/src/js')
-rw-r--r--addons/web/static/src/js/core/dom.js91
-rw-r--r--addons/web/static/src/js/fields/basic_fields.js12
-rw-r--r--addons/web/static/src/js/fields/relational_fields.js1
-rw-r--r--addons/web/static/src/js/views/abstract_view.js1
-rw-r--r--addons/web/static/src/js/views/basic/basic_model.js4
-rw-r--r--addons/web/static/src/js/widgets/date_picker.js13
-rw-r--r--addons/web/static/src/js/widgets/dropdown_menu.js10
7 files changed, 129 insertions, 3 deletions
diff --git a/addons/web/static/src/js/core/dom.js b/addons/web/static/src/js/core/dom.js
index 2a961596862..848e406c4b2 100644
--- a/addons/web/static/src/js/core/dom.js
+++ b/addons/web/static/src/js/core/dom.js
@@ -20,6 +20,7 @@ odoo.define('web.dom', function (require) {
* something happens in the DOM.
*/
+var concurrency = require('web.concurrency');
var config = require('web.config');
var core = require('web.core');
var _t = core._t;
@@ -40,7 +41,9 @@ function _notify(content, callbacks) {
core.bus.trigger('DOM_updated', content);
}
-return {
+var dom = {
+ DEBOUNCE: 400,
+
/**
* Appends content in a jQuery object and optionnally triggers an event
*
@@ -209,6 +212,91 @@ return {
return position;
},
/**
+ * Protects a function which is to be used as a handler by preventing its
+ * execution for the duration of a previous call to it (including async
+ * parts of that call).
+ *
+ * Limitation: as the handler is ignored during async actions,
+ * the 'preventDefault' or 'stopPropagation' calls it may want to do
+ * will be ignored too. Using the 'preventDefault' and 'stopPropagation'
+ * arguments solves that problem.
+ *
+ * @param {function} fct
+ * The function which is to be used as a handler. If a promise
+ * is returned, it is used to determine when the handler's action is
+ * finished. Otherwise, the return is used as jQuery uses it.
+ * @param {function|boolean} preventDefault
+ * @param {function|boolean} stopPropagation
+ */
+ makeAsyncHandler: function (fct, preventDefault, stopPropagation) {
+ // Create a deferred indicating if a previous call to this handler is
+ // still pending.
+ var def = $.when();
+
+ return function (ev) {
+ if (preventDefault === true || preventDefault && preventDefault()) {
+ ev.preventDefault();
+ }
+ if (stopPropagation === true || stopPropagation && stopPropagation()) {
+ ev.stopPropagation();
+ }
+
+ if (def.state() === 'pending') {
+ // If a previous call to this handler is still pending, ignore
+ // the new call.
+ return;
+ }
+ var result = fct.apply(this, arguments);
+ def = $.when(result);
+ return result;
+ };
+ },
+ /**
+ * Creates a debounced version of a function to be used as a button click
+ * handler. Also improves the handler to disable the button for the time of
+ * the debounce and/or the time of the async actions it performs.
+ *
+ * Limitation: if two handlers are put on the same button, the button will
+ * become enabled again once any handler's action finishes (multiple click
+ * handlers should however not be binded to the same button).
+ *
+ * @param {function} fct
+ * The function which is to be used as a button click handler. If a
+ * promise is returned, it is used to determine when the button can be
+ * re-enabled. Otherwise, the return is used as jQuery uses it.
+ */
+ makeButtonHandler: function (fct) {
+ // Fallback: if the final handler is not binded to a button, at least
+ // make it an async handler (also handles the case where some events
+ // might ignore the disabled state of the button).
+ fct = dom.makeAsyncHandler(fct);
+
+ return function (ev) {
+ var result = fct.apply(this, arguments);
+
+ var $button = $(ev.target).closest('.btn');
+ if (!$button.length) {
+ return result;
+ }
+
+ // Disable the button for the duration of the handler's action
+ // or at least for the duration of the click debounce. This makes
+ // a 'real' debounce creation useless. Also, during the debouncing
+ // part, the button is disabled without any visual effect.
+ $button.addClass('o_debounce_disabled');
+ $.when(dom.DEBOUNCE && concurrency.delay(dom.DEBOUNCE)).then(function () {
+ $button.addClass('disabled').prop('disabled', true);
+ $button.removeClass('o_debounce_disabled');
+
+ return $.when(result).always(function () {
+ $button.removeClass('disabled').prop('disabled', false);
+ });
+ });
+
+ return result;
+ };
+ },
+ /**
* Prepends content in a jQuery object and optionnally triggers an event
*
* @param {jQuery} [$target] the node where content will be prepended
@@ -460,4 +548,5 @@ return {
}
},
};
+return dom;
});
diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js
index 53fbf99a8cd..aac91081c3c 100644
--- a/addons/web/static/src/js/fields/basic_fields.js
+++ b/addons/web/static/src/js/fields/basic_fields.js
@@ -2409,6 +2409,18 @@ var FieldProgressBar = AbstractField.extend({
this.$('.o_progressbar_value').focus().select();
}
},
+ /**
+ * The progress bar has more than one field/value to deal with
+ * i.e. max_value
+ *
+ * @override
+ * @private
+ */
+ _reset: function () {
+ this._super.apply(this, arguments);
+ var new_max_value = this.recordData[this.nodeOptions.max_value];
+ this.max_value = new_max_value !== undefined ? new_max_value : this.max_value;
+ },
isSet: function () {
return true;
},
diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js
index d9e15dab302..60b0275c8c8 100644
--- a/addons/web/static/src/js/fields/relational_fields.js
+++ b/addons/web/static/src/js/fields/relational_fields.js
@@ -564,6 +564,7 @@ var FieldMany2One = AbstractField.extend({
initial_ids: ids ? _.map(ids, function (x) { return x[0]; }) : undefined,
initial_view: view,
disable_multiple_selection: true,
+ no_create: !self.can_create,
on_selected: function (records) {
self.reinitialize(records[0]);
self.activate();
diff --git a/addons/web/static/src/js/views/abstract_view.js b/addons/web/static/src/js/views/abstract_view.js
index 8f9f167c69f..024216b5619 100644
--- a/addons/web/static/src/js/views/abstract_view.js
+++ b/addons/web/static/src/js/views/abstract_view.js
@@ -181,6 +181,7 @@ var AbstractView = Class.extend({
modelName: params.modelName,
res_id: params.currentId,
res_ids: params.ids,
+ orderedBy: params.context ? params.context.orderedBy : [],
};
if (params.modelName) {
this.loadParams.modelName = params.modelName;
diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js
index 345b76856b6..53aa2e56c5a 100644
--- a/addons/web/static/src/js/views/basic/basic_model.js
+++ b/addons/web/static/src/js/views/basic/basic_model.js
@@ -3674,7 +3674,7 @@ var BasicModel = AbstractModel.extend({
model: params.modelName,
offset: params.offset || (type === 'record' ? _.indexOf(res_ids, res_id) : 0),
openGroupByDefault: params.openGroupByDefault,
- orderedBy: params.orderedBy || (params.context && params.context.orderedBy) || [],
+ orderedBy: params.orderedBy || [],
orderedResIDs: params.orderedResIDs,
parentID: params.parentID,
rawContext: params.rawContext,
@@ -4608,7 +4608,7 @@ var BasicModel = AbstractModel.extend({
if (orderData1 > orderData2) {
return order.asc ? 1 : -1;
}
- return compareRecords(record1ID, record2ID, level + 1);
+ return compareRecords(resId1, resId2, level + 1);
};
utils.stableSort(list.res_ids, compareRecords);
}
diff --git a/addons/web/static/src/js/widgets/date_picker.js b/addons/web/static/src/js/widgets/date_picker.js
index 7ba3010a3b5..b677abe377a 100644
--- a/addons/web/static/src/js/widgets/date_picker.js
+++ b/addons/web/static/src/js/widgets/date_picker.js
@@ -15,6 +15,7 @@ var DateWidget = Widget.extend({
'change.datetimepicker': 'changeDatetime',
'change .o_datepicker_input': 'changeDatetime',
'input input': '_onInput',
+ 'show.datetimepicker': '_onDateTimePickerShow',
},
/**
* @override
@@ -226,6 +227,18 @@ var DateWidget = Widget.extend({
//--------------------------------------------------------------------------
/**
+ * Reacts to the datetimepicker being shown
+ * Could set/verify our widget value
+ * And subsequently update the datetimepicker
+ *
+ * @private
+ */
+ _onDateTimePickerShow: function () {
+ if (this.$input.val().length !== 0 && this.isValid()) {
+ this.$input.select();
+ }
+ },
+ /**
* Prevents 'input' events triggered by the library to bubble up, as they
* might have unwanted effects (like triggering 'field_changed' events in
* the context of field widgets)
diff --git a/addons/web/static/src/js/widgets/dropdown_menu.js b/addons/web/static/src/js/widgets/dropdown_menu.js
index fdab9b82ebf..e92c714d838 100644
--- a/addons/web/static/src/js/widgets/dropdown_menu.js
+++ b/addons/web/static/src/js/widgets/dropdown_menu.js
@@ -15,6 +15,7 @@ var DropdownMenu = Widget.extend({
'click .o_item_option': '_onOptionClick',
'click span.o_trash_button': '_onTrashClick',
'hidden.bs.dropdown': '_onBootstrapClose',
+ 'click .dropdown-item-text': '_onDropDownItemTextClick',
},
/**
@@ -200,6 +201,15 @@ var DropdownMenu = Widget.extend({
this._renderMenuItems();
},
/**
+ * Reacts to click on bootstrap's dropdown-item-text
+ * Protects against Bootstrap dropdown close from inside click
+ *
+ * @private
+ */
+ _onDropDownItemTextClick: function (ev) {
+ ev.stopPropagation();
+ },
+ /**
* @private
* @param {MouseEvent} event
*/