summaryrefslogtreecommitdiff
path: root/addons/web/static/src
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web/static/src')
-rw-r--r--addons/web/static/src/js/framework/domain_utils.js19
-rw-r--r--addons/web/static/src/js/views/form_widgets.js19
-rw-r--r--addons/web/static/src/js/views/list_view_editable.js3
-rw-r--r--addons/web/static/src/js/views/search_view.js2
-rw-r--r--addons/web/static/src/js/widgets/domain_selector.js17
-rw-r--r--addons/web/static/src/js/widgets/domain_selector_dialog.js48
-rw-r--r--addons/web/static/src/less/domain_selector.less5
-rw-r--r--addons/web/static/src/less/form_view_layout.less16
-rw-r--r--addons/web/static/src/xml/base.xml17
9 files changed, 124 insertions, 22 deletions
diff --git a/addons/web/static/src/js/framework/domain_utils.js b/addons/web/static/src/js/framework/domain_utils.js
new file mode 100644
index 00000000000..8cefb7cabbe
--- /dev/null
+++ b/addons/web/static/src/js/framework/domain_utils.js
@@ -0,0 +1,19 @@
+odoo.define("web.domainUtils", function (require) {
+"use strict";
+
+var pyeval = require("web.pyeval");
+
+function domainToString(domain) {
+ if (_.isString(domain)) return domain;
+ return JSON.stringify(domain || []).replace(/false/g, "False").replace(/true/g, "True");
+}
+function stringToDomain(domain) {
+ if (!_.isString(domain)) return domain;
+ return pyeval.eval("domain", domain || "[]");
+}
+
+return {
+ domainToString: domainToString,
+ stringToDomain: stringToDomain,
+};
+});
diff --git a/addons/web/static/src/js/views/form_widgets.js b/addons/web/static/src/js/views/form_widgets.js
index a3c7bcaaff3..78d8ea7c5d1 100644
--- a/addons/web/static/src/js/views/form_widgets.js
+++ b/addons/web/static/src/js/views/form_widgets.js
@@ -11,6 +11,7 @@ var Priority = require('web.Priority');
var ProgressBar = require('web.ProgressBar');
var Dialog = require('web.Dialog');
var DomainSelector = require("web.DomainSelector");
+var DomainSelectorDialog = require("web.DomainSelectorDialog");
var common = require('web.form_common');
var formats = require('web.formats');
var framework = require('web.framework');
@@ -400,11 +401,19 @@ var FieldDomain = common.AbstractField.extend(common.ReinitializeFieldMixin).ext
e.preventDefault();
this._showSelection();
},
+ "click .o_form_field_domain_dialog_button": function (e) {
+ e.preventDefault();
+ this.openDomainDialog();
+ },
},
custom_events: {
"domain_changed": function (e) {
+ if (this.options.in_dialog) return;
this.set_value(this.domainSelector.getDomain(), true);
},
+ "domain_selected": function (e) {
+ this.set_value(e.data.domain);
+ },
},
init: function () {
this._super.apply(this, arguments);
@@ -412,6 +421,7 @@ var FieldDomain = common.AbstractField.extend(common.ReinitializeFieldMixin).ext
this.valid = true;
this.debug = session.debug;
this.options = _.defaults(this.options || {}, {
+ in_dialog: false,
model: undefined, // this option is mandatory !
fs_filters: {}, // Field selector filters (to only show a subset of available fields @see FieldSelector)
});
@@ -457,7 +467,7 @@ var FieldDomain = common.AbstractField.extend(common.ReinitializeFieldMixin).ext
this.domainSelector.destroy();
}
this.domainSelector = new DomainSelector(this, this.model, domain, {
- readonly: this.get("effective_readonly"),
+ readonly: this.get("effective_readonly") || this.options.in_dialog,
fs_filters: this.options.fs_filters,
debugMode: session.debug,
});
@@ -492,6 +502,13 @@ var FieldDomain = common.AbstractField.extend(common.ReinitializeFieldMixin).ext
disable_multiple_selection: true,
}).open();
},
+ openDomainDialog: function () {
+ new DomainSelectorDialog(this, this.model, this.get("value"), {
+ readonly: this.get("effective_readonly"),
+ fs_filters: this.options.fs_filters,
+ debugMode: session.debug,
+ }).open();
+ },
});
var FieldDate = common.AbstractField.extend(common.ReinitializeFieldMixin, {
diff --git a/addons/web/static/src/js/views/list_view_editable.js b/addons/web/static/src/js/views/list_view_editable.js
index 3f5d39e60e1..ef9bf1a6062 100644
--- a/addons/web/static/src/js/views/list_view_editable.js
+++ b/addons/web/static/src/js/views/list_view_editable.js
@@ -641,7 +641,8 @@ ListView.include(/** @lends instance.web.ListView# */{
if (saveInfo.created) {
return self.start_edition();
}
- var record = self.records[next_record](saveInfo.record);
+ var options = { wraparound: !self.is_action_enabled('create') };
+ var record = self.records[next_record](saveInfo.record, options);
if (record === undefined) {
return self.start_edition();
}
diff --git a/addons/web/static/src/js/views/search_view.js b/addons/web/static/src/js/views/search_view.js
index 9f985fc3f49..65dbb4557e7 100644
--- a/addons/web/static/src/js/views/search_view.js
+++ b/addons/web/static/src/js/views/search_view.js
@@ -466,7 +466,7 @@ var SearchView = View.extend({
*/
select_completion: function (e, ui) {
e.preventDefault();
- if(ui.item.facet.values && ui.item.facet.values.length && String(ui.item.facet.values[0].value).trim() !== "") {
+ if(ui.item.facet && ui.item.facet.values && ui.item.facet.values.length && String(ui.item.facet.values[0].value).trim() !== "") {
this.query.add(ui.item.facet);
} else {
this.query.trigger('add');
diff --git a/addons/web/static/src/js/widgets/domain_selector.js b/addons/web/static/src/js/widgets/domain_selector.js
index 5f4287a8d8d..15e967ff4d4 100644
--- a/addons/web/static/src/js/widgets/domain_selector.js
+++ b/addons/web/static/src/js/widgets/domain_selector.js
@@ -3,9 +3,9 @@ odoo.define("web.DomainSelector", function (require) {
var core = require("web.core");
var datepicker = require("web.datepicker");
+var domainUtils = require("web.domainUtils");
var formats = require ("web.formats");
var ModelFieldSelector = require("web.ModelFieldSelector");
-var pyeval = require("web.pyeval");
var Widget = require("web.Widget");
var _t = core._t;
@@ -55,7 +55,7 @@ var DomainNode = Widget.extend({
},
/// A DomainNode needs a model and domain to work. It can also receives a set of options
/// @param model - a string with the model name
- /// @param domain - an array of the prefix representation of the domain
+ /// @param domain - an array of the prefix representation of the domain (or a string which represents it)
/// @param options - an object with possible values:
/// - readonly, a boolean to indicate if the widget is readonly or not (default to true)
/// - fs_filters, an object with a series of filters to use for the field selector (see @FieldSelector)
@@ -109,7 +109,7 @@ var DomainTree = DomainNode.extend({
/// @see DomainTree._addFlattenedChildren
init: function (parent, model, domain, options) {
this._super.apply(this, arguments);
- this._initialize(domain);
+ this._initialize(domainUtils.stringToDomain(domain));
},
/// @see DomainTree.init
_initialize: function (domain) {
@@ -265,8 +265,9 @@ var DomainSelector = DomainTree.extend({
/// the widget is re-rendered and notifies the parents. If not, a warning is shown to the
/// user and the input is ignored.
"change .o_domain_debug_input": function (e) {
+ var domain;
try {
- var domain = pyeval.eval("domain", $(e.currentTarget).val() || "[]");
+ domain = domainUtils.stringToDomain($(e.currentTarget).val());
} catch (err) {
this.do_warn(_t("Syntax error"), _t("The domain you entered is not properly formed"));
return;
@@ -292,12 +293,7 @@ var DomainSelector = DomainTree.extend({
// Display technical domain if in debug mode
this.$debugInput = this.$(".o_domain_debug_input");
if (this.$debugInput.length) {
- var strValue = this.getDomain() || "[]";
- if (!_.isString(strValue)) { // TODO there is no "pyuneval" function
- strValue = JSON.stringify(strValue);
- strValue = strValue.replace(/false/g, "False").replace(/true/g, "True");
- }
- this.$debugInput.val(strValue);
+ this.$debugInput.val(domainUtils.domainToString(this.getDomain()));
}
},
});
@@ -328,6 +324,7 @@ var DomainLeaf = DomainNode.extend({
init: function (parent, model, domain, options) {
this._super.apply(this, arguments);
+ domain = domainUtils.stringToDomain(domain);
this.chain = domain[0][0];
this.operator = domain[0][1];
this.value = domain[0][2];
diff --git a/addons/web/static/src/js/widgets/domain_selector_dialog.js b/addons/web/static/src/js/widgets/domain_selector_dialog.js
new file mode 100644
index 00000000000..47bfa4e1359
--- /dev/null
+++ b/addons/web/static/src/js/widgets/domain_selector_dialog.js
@@ -0,0 +1,48 @@
+odoo.define("web.DomainSelectorDialog", function (require) {
+"use strict";
+
+var core = require("web.core");
+var Dialog = require("web.Dialog");
+var DomainSelector = require("web.DomainSelector");
+
+var _t = core._t;
+
+return Dialog.extend({
+ init: function (parent, model, domain, options) {
+ this.model = model;
+ this.options = _.extend({
+ readonly: true,
+ fs_filters: {},
+ debugMode: false,
+ }, options || {});
+
+ var buttons;
+ if (this.options.readonly) {
+ buttons = [
+ {text: _t("Close"), close: true},
+ ];
+ } else {
+ buttons = [
+ {text: _t("Save"), classes: "btn-primary", close: true, click: function () {
+ this.trigger_up("domain_selected", {domain: this.domainSelector.getDomain()});
+ }},
+ {text: _t("Discard"), close: true},
+ ];
+ }
+
+ this._super(parent, _.extend({}, {
+ title: _t("Domain"),
+ buttons: buttons,
+ }, options || {}));
+
+ this.domainSelector = new DomainSelector(this, model, domain, options);
+ },
+ start: function () {
+ this.$el.css("overflow", "visible").closest(".modal-dialog").css("height", "auto"); // This restores default modal height (bootstrap) and allows field selector to overflow
+ return $.when(
+ this._super.apply(this, arguments),
+ this.domainSelector.appendTo(this.$el)
+ );
+ },
+});
+});
diff --git a/addons/web/static/src/less/domain_selector.less b/addons/web/static/src/less/domain_selector.less
index 7dced900b21..f3917526ec5 100644
--- a/addons/web/static/src/less/domain_selector.less
+++ b/addons/web/static/src/less/domain_selector.less
@@ -24,7 +24,10 @@
&.o_domain_selector {
&.o_edit_mode {
position: relative;
- padding-right: @o-domain-selector-panel-space;
+
+ > .o_domain_node_children_container {
+ padding-right: @o-domain-selector-panel-space;
+ }
}
> .o_domain_node_children_container {
diff --git a/addons/web/static/src/less/form_view_layout.less b/addons/web/static/src/less/form_view_layout.less
index 281cf0d217e..fc5522035e5 100644
--- a/addons/web/static/src/less/form_view_layout.less
+++ b/addons/web/static/src/less/form_view_layout.less
@@ -91,7 +91,7 @@
// Title
.oe_title {
> h1, > h2, > h3 {
- width: 100%; // Needed because inline-block when is a hx.o_row
+ width: 100%; // Needed because inline-block when is a hx.o_row
margin-top: 0;
margin-bottom: 0;
line-height: inherit;
@@ -276,6 +276,20 @@
padding: 0 1em;
}
}
+
+ .o_form_field_domain {
+ > .o_form_field_domain_panel {
+ margin-top: 8px;
+ }
+ &.o_inline_mode.o_edit_mode {
+ position: relative;
+
+ > .o_form_field_domain_panel {
+ .o-position-absolute(0, 0);
+ margin-top: 0;
+ }
+ }
+ }
}
// Overriden style when form view in modal
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index f6ac60c4d7e..2ee261a3bdd 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -670,13 +670,16 @@
t-att-maxlength="widget.field.size"/>
</t>
-<div t-name="FieldDomain" class="o_form_field_domain">
+<div t-name="FieldDomain" t-attf-class="o_form_field_domain#{widget.get('effective_readonly') ? '' : ' o_edit_mode'}#{widget.options.in_dialog ? '' : ' o_inline_mode'}">
<!-- domain selector will be instantiated here -->
- <div class="mt8">
+ <div class="o_form_field_domain_panel">
<i class="fa fa-arrow-right"/>
- <span><span class="o_domain_records_count"/> Records Selected</span>
- <button class="btn btn-default btn-xs o_domain_show_selection_button" type="button">See selection</button>
+ <button class="btn btn-xs btn-default o_domain_show_selection_button" type="button">
+ <span class="o_domain_records_count"/> record(s)
+ </button>
<span class="text-warning o_domain_error_message hidden"><i class="fa fa-exclamation-triangle"/> Invalid domain</span>
+ <button t-if="widget.options.in_dialog &amp;&amp; !widget.get('effective_readonly')"
+ class="btn btn-sm btn-primary o_form_field_domain_dialog_button">Edit domain</button>
</div>
</div>
@@ -684,7 +687,7 @@
<div t-if="!widget.readonly &amp;&amp; !widget.noControlPanel" class="btn-group btn-group-sm pull-right o_domain_node_control_panel">
<button class="btn btn-default o_domain_delete_node_button"><i class="fa fa-minus"/></button>
<button class="btn btn-default o_domain_add_node_button"><i class="fa fa-plus"/></button>
- <button class="btn btn-default o_domain_add_node_button" t-att-data-branch="1"><i class="fa fa-ellipsis-h"/></button>
+ <button class="btn btn-default o_domain_add_node_button" data-branch="1"><i class="fa fa-ellipsis-h"/></button>
</div>
</t>
<t t-name="DomainTree.OperatorSelector">
@@ -712,9 +715,9 @@
</t>
<t t-else="">
<div>
- <t t-if="widget.children.length === 1">Match the records with the following rule:</t>
+ <t t-if="widget.children.length === 1">Match records with the following rule:</t>
<t t-else="">
- <span>Match the records with</span>
+ <span>Match records with</span>
<t t-call="DomainTree.OperatorSelector"/>
<span>of the following rules:</span>
</t>