mirror of https://github.com/OCA/web.git
web_search_with_and: Migration to 14.0
parent
1465517c3c
commit
1c45bbc5a6
|
@ -23,7 +23,7 @@ Use AND conditions on omnibar search
|
||||||
:target: https://runbot.odoo-community.org/runbot/162/13.0
|
:target: https://runbot.odoo-community.org/runbot/162/13.0
|
||||||
:alt: Try me on Runbot
|
:alt: Try me on Runbot
|
||||||
|
|
||||||
|badge1| |badge2| |badge3| |badge4| |badge5|
|
|badge1| |badge2| |badge3| |badge4| |badge5|
|
||||||
|
|
||||||
When searching for records on same field Odoo joins multiple queries with OR.
|
When searching for records on same field Odoo joins multiple queries with OR.
|
||||||
For example:
|
For example:
|
||||||
|
@ -79,6 +79,7 @@ Contributors
|
||||||
* Francesco Apruzzese <f.apruzzese@apuliasoftware.it>
|
* Francesco Apruzzese <f.apruzzese@apuliasoftware.it>
|
||||||
* Numigi (tm) and all its contributors (https://bit.ly/numigiens)
|
* Numigi (tm) and all its contributors (https://bit.ly/numigiens)
|
||||||
* Souheil Bejaoui <souheil.bejaoui@acsone.eu>
|
* Souheil Bejaoui <souheil.bejaoui@acsone.eu>
|
||||||
|
* Pedro Guirao <pedro@serincloud.com>
|
||||||
|
|
||||||
Maintainers
|
Maintainers
|
||||||
~~~~~~~~~~~
|
~~~~~~~~~~~
|
||||||
|
|
|
@ -4,11 +4,11 @@
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "Use AND conditions on omnibar search",
|
"name": "Use AND conditions on omnibar search",
|
||||||
"version": "13.0.1.0.0",
|
"version": "14.0.1.0.0",
|
||||||
"author": "Versada UAB, ACSONE SA/NV, Odoo Community Association (OCA)",
|
"author": "Versada UAB, ACSONE SA/NV, Serincloud, Odoo Community Association (OCA)",
|
||||||
"license": "AGPL-3",
|
"license": "AGPL-3",
|
||||||
"category": "web",
|
"category": "web",
|
||||||
"website": "https://github.com/OCA/web",
|
"website": "https://github.com/OCA/web",
|
||||||
"depends": ["web"],
|
"depends": ["web"],
|
||||||
"data": ["data/data.xml"],
|
"data": ["views/assets.xml"],
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,3 +3,4 @@
|
||||||
* Francesco Apruzzese <f.apruzzese@apuliasoftware.it>
|
* Francesco Apruzzese <f.apruzzese@apuliasoftware.it>
|
||||||
* Numigi (tm) and all its contributors (https://bit.ly/numigiens)
|
* Numigi (tm) and all its contributors (https://bit.ly/numigiens)
|
||||||
* Souheil Bejaoui <souheil.bejaoui@acsone.eu>
|
* Souheil Bejaoui <souheil.bejaoui@acsone.eu>
|
||||||
|
* Pedro Guirao <pedro.guirao@ingenieriacloud.com>
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
odoo.define(
|
||||||
|
"web_search_with_and/static/src/js/control_panel_model_extension.js",
|
||||||
|
function (require) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const {patch} = require("web.utils");
|
||||||
|
const components = {
|
||||||
|
ControlPanelModelExtension: require("web/static/src/js/control_panel/control_panel_model_extension.js"),
|
||||||
|
};
|
||||||
|
|
||||||
|
patch(
|
||||||
|
components.ControlPanelModelExtension,
|
||||||
|
"web_search_with_and/static/src/js/control_panel_model_extension.js",
|
||||||
|
{
|
||||||
|
addAutoCompletionValues({
|
||||||
|
filterId,
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
operator,
|
||||||
|
isShiftKey,
|
||||||
|
}) {
|
||||||
|
const queryElem = this.state.query.find(
|
||||||
|
(queryElem) =>
|
||||||
|
queryElem.filterId === filterId &&
|
||||||
|
queryElem.value === value &&
|
||||||
|
queryElem.operator === operator
|
||||||
|
);
|
||||||
|
if (!queryElem) {
|
||||||
|
if (isShiftKey) {
|
||||||
|
const groupId = Math.random();
|
||||||
|
this.state.query.push({
|
||||||
|
filterId,
|
||||||
|
groupId,
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
operator,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const {groupId} = this.state.filters[filterId];
|
||||||
|
this.state.query.push({
|
||||||
|
filterId,
|
||||||
|
groupId,
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
operator,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
queryElem.label = label;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
|
@ -1,39 +0,0 @@
|
||||||
odoo.define("web_search_with_and", function (require) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var searchBarAutocompleteRegistry = require("web.search_bar_autocomplete_sources_registry");
|
|
||||||
var SearchBar = require("web.SearchBar");
|
|
||||||
|
|
||||||
SearchBar.include({
|
|
||||||
// Override the base method to detect a "shift" event
|
|
||||||
_onAutoCompleteSelected: function (e, ui) {
|
|
||||||
var values = ui.item.facet.values;
|
|
||||||
if (
|
|
||||||
e.shiftKey &&
|
|
||||||
values &&
|
|
||||||
values.length &&
|
|
||||||
String(values[0].value).trim() !== ""
|
|
||||||
) {
|
|
||||||
// In case of an "AND" search a new facet is added regarding of
|
|
||||||
// the previous facets
|
|
||||||
e.preventDefault();
|
|
||||||
var filter = ui.item.facet.filter;
|
|
||||||
var field = this.fields[filter.attrs.name];
|
|
||||||
var Obj = searchBarAutocompleteRegistry.getAny([
|
|
||||||
filter.attrs.widget,
|
|
||||||
field.type,
|
|
||||||
]);
|
|
||||||
var obj = new Obj(this, filter, field, this.actionContext);
|
|
||||||
var new_filter = Object.assign({}, ui.item.facet.filter, {
|
|
||||||
domain: obj.getDomain(values),
|
|
||||||
autoCompleteValues: values,
|
|
||||||
});
|
|
||||||
this.trigger_up("new_filters", {
|
|
||||||
filters: [new_filter],
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return this._super.apply(this, arguments);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
odoo.define("web_search_with_and/static/src/js/search_bar.js", function (require) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const {patch} = require("web.utils");
|
||||||
|
const components = {
|
||||||
|
SearchBar: require("web.SearchBar"),
|
||||||
|
};
|
||||||
|
|
||||||
|
patch(components.SearchBar, "web_search_with_and/static/src/js/search_bar.js", {
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @param {Object} source
|
||||||
|
*/
|
||||||
|
_selectSource(source) {
|
||||||
|
// Inactive sources are:
|
||||||
|
// - Selection sources
|
||||||
|
// - "no result" items
|
||||||
|
if (source.active) {
|
||||||
|
const labelValue = source.label || this.state.inputValue;
|
||||||
|
console.log(
|
||||||
|
"------------- addAutoCompletionValues11 -------------",
|
||||||
|
source,
|
||||||
|
this.isShiftKey
|
||||||
|
);
|
||||||
|
this.model.dispatch("addAutoCompletionValues", {
|
||||||
|
filterId: source.filterId,
|
||||||
|
value:
|
||||||
|
"value" in source
|
||||||
|
? source.value
|
||||||
|
: this._parseWithSource(labelValue, source),
|
||||||
|
label: labelValue,
|
||||||
|
operator: source.filterOperator || source.operator,
|
||||||
|
isShiftKey: this.isShiftKey,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this._closeAutoComplete();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @param {KeyboardEvent} ev
|
||||||
|
*/
|
||||||
|
_onSearchKeydown(ev) {
|
||||||
|
if (ev.isComposing) {
|
||||||
|
// This case happens with an IME for example: we let it handle all key events.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ev.shiftKey) {
|
||||||
|
this.isShiftKey = true;
|
||||||
|
} else {
|
||||||
|
this.isShiftKey = false;
|
||||||
|
}
|
||||||
|
const currentItem = this.state.sources[this.state.focusedItem] || {};
|
||||||
|
switch (ev.key) {
|
||||||
|
case "ArrowDown":
|
||||||
|
ev.preventDefault();
|
||||||
|
if (Object.keys(this.state.sources).length) {
|
||||||
|
let nextIndex = this.state.focusedItem + 1;
|
||||||
|
if (nextIndex >= this.state.sources.length) {
|
||||||
|
nextIndex = 0;
|
||||||
|
}
|
||||||
|
this.state.focusedItem = nextIndex;
|
||||||
|
} else {
|
||||||
|
this.env.bus.trigger("focus-view");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "ArrowLeft":
|
||||||
|
if (currentItem.expanded) {
|
||||||
|
// Priority 1: fold expanded item.
|
||||||
|
ev.preventDefault();
|
||||||
|
this._expandSource(currentItem, false);
|
||||||
|
} else if (currentItem.parent) {
|
||||||
|
// Priority 2: focus parent item.
|
||||||
|
ev.preventDefault();
|
||||||
|
this.state.focusedItem = this.state.sources.indexOf(
|
||||||
|
currentItem.parent
|
||||||
|
);
|
||||||
|
// Priority 3: Do nothing (navigation inside text).
|
||||||
|
} else if (ev.target.selectionStart === 0) {
|
||||||
|
// Priority 4: navigate to rightmost facet.
|
||||||
|
this._focusFacet(this.model.get("facets").length - 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "ArrowRight":
|
||||||
|
if (ev.target.selectionStart === this.state.inputValue.length) {
|
||||||
|
// Priority 1: Do nothing (navigation inside text).
|
||||||
|
if (currentItem.expand) {
|
||||||
|
// Priority 2: go to first child or expand item.
|
||||||
|
ev.preventDefault();
|
||||||
|
if (currentItem.expanded) {
|
||||||
|
this.state.focusedItem++;
|
||||||
|
} else {
|
||||||
|
this._expandSource(currentItem, true);
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
ev.target.selectionStart === this.state.inputValue.length
|
||||||
|
) {
|
||||||
|
// Priority 3: navigate to leftmost facet.
|
||||||
|
this._focusFacet(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "ArrowUp":
|
||||||
|
ev.preventDefault();
|
||||||
|
let previousIndex = this.state.focusedItem - 1;
|
||||||
|
if (previousIndex < 0) {
|
||||||
|
previousIndex = this.state.sources.length - 1;
|
||||||
|
}
|
||||||
|
this.state.focusedItem = previousIndex;
|
||||||
|
break;
|
||||||
|
case "Backspace":
|
||||||
|
if (!this.state.inputValue.length) {
|
||||||
|
const facets = this.model.get("facets");
|
||||||
|
if (facets.length) {
|
||||||
|
this._onFacetRemove(facets[facets.length - 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "Enter":
|
||||||
|
if (!this.state.inputValue.length) {
|
||||||
|
this.model.dispatch("search");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Falls through */
|
||||||
|
case "Tab":
|
||||||
|
if (this.state.inputValue.length) {
|
||||||
|
ev.preventDefault(); // Keep the focus inside the search bar
|
||||||
|
this._selectSource(currentItem);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "Escape":
|
||||||
|
if (this.state.sources.length) {
|
||||||
|
this._closeAutoComplete();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,5 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
|
|
||||||
<template
|
<template
|
||||||
id="assets_backend"
|
id="assets_backend"
|
||||||
name="web_view_editor assets"
|
name="web_view_editor assets"
|
||||||
|
@ -8,8 +9,13 @@
|
||||||
<xpath expr="." position="inside">
|
<xpath expr="." position="inside">
|
||||||
<script
|
<script
|
||||||
type="text/javascript"
|
type="text/javascript"
|
||||||
src="/web_search_with_and/static/src/js/search.js"
|
src="/web_search_with_and/static/src/js/control_panel_model_extension.js"
|
||||||
|
/>
|
||||||
|
<script
|
||||||
|
type="text/javascript"
|
||||||
|
src="/web_search_with_and/static/src/js/search_bar.js"
|
||||||
/>
|
/>
|
||||||
</xpath>
|
</xpath>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
Loading…
Reference in New Issue