[REF] web(site): migrate jQuery.autocomplete to owl component

Adapt the website industry selection autocomplete inside the
configurator to use the autocomplete owl component instead of jquery
autocomplete

task-3439226

Part-of: odoo/odoo#139209
This commit is contained in:
Romain Estievenart
2023-11-02 21:02:44 +00:00
parent 86a9171ec7
commit 99f5ae32d3
4 changed files with 55 additions and 82 deletions
@@ -64,9 +64,7 @@ export class AutoComplete extends Component {
// position and size
if (this.props.dropdown) {
usePosition("sourcesList", () => this.targetDropdown, {
position: "bottom-start",
});
usePosition("sourcesList", () => this.targetDropdown, this.dropdownOptions);
} else {
this.open(false);
}
@@ -76,6 +74,12 @@ export class AutoComplete extends Component {
return this.inputRef.el;
}
get dropdownOptions() {
return {
position: "bottom-start",
};
}
get isOpened() {
return this.state.open;
}
@@ -1,5 +1,6 @@
/** @odoo-module **/
import { AutoComplete } from "@web/core/autocomplete/autocomplete";
import { delay } from "@web/core/utils/concurrency";
import { getDataURLFromFile } from "@web/core/utils/urls";
import weUtils from '@web_editor/js/common/utils';
@@ -92,12 +93,25 @@ Object.assign(WelcomeScreen, {
template: 'website.Configurator.WelcomeScreen',
});
class IndustrySelectionAutoComplete extends AutoComplete {
static timeout = 400;
get dropdownOptions() {
return {
...super.dropdownOptions,
position: "bottom-fit",
}
}
get ulDropdownClass() {
return `${super.ulDropdownClass} custom-ui-autocomplete shadow-lg border-0 o_configurator_show_fast o_configurator_industry_dropdown`
}
}
class DescriptionScreen extends Component {
setup() {
this.industrySelection = useRef('industrySelection');
this.state = useStore();
this.labelToId = {};
this.autocompleteHasResults = true;
this.orm = useService('orm');
onMounted(() => this.onMounted());
@@ -105,42 +119,29 @@ class DescriptionScreen extends Component {
onMounted() {
this.selectWebsitePurpose();
$(this.industrySelection.el).autocomplete({
appendTo: '.o_configurator_industry_wrapper',
delay: 400,
minLength: 1,
source: this._autocompleteSearch.bind(this),
select: this._selectIndustry.bind(this),
classes: {
'ui-autocomplete': 'custom-ui-autocomplete shadow-lg border-0 o_configurator_show_fast o_configurator_industry_dropdown',
},
});
if (this.state.selectedIndustry) {
this.industrySelection.el.value = this.state.selectedIndustry.label;
this.industrySelection.el.parentNode.dataset.value = this.state.selectedIndustry.label;
this.labelToId[this.state.selectedIndustry.label] = this.state.selectedIndustry.id;
}
}
/**
* Set the input's parent label value to automatically adapt input size
* and update the selected industry.
*
* @private
* @param {String} label an industry label
* @param {Object} suggestion an industry
*/
_setSelectedIndustry(label) {
this.industrySelection.el.parentNode.dataset.value = label;
if (!this.autocompleteHasResults) {
// Unknown industry.
this.state.selectIndustry(label, -1);
} else {
const id = this.labelToId[label];
this.state.selectIndustry(label, id);
}
_setSelectedIndustry(suggestion) {
const { label, id } = Object.getPrototypeOf(suggestion);
this.state.selectIndustry(label, id);
this.checkDescriptionCompletion();
}
get sources() {
return [
{
options: (request) => {
return request.length < 1 ? [] : this._autocompleteSearch(request);
},
},
];
}
/**
* Called each time the autocomplete input's value changes. Only industries
* having a label or a synonym containing all terms of the input value are
@@ -150,13 +151,10 @@ class DescriptionScreen extends Component {
* sorted alphabetically.
* The result size is limited to 30.
*
* @param {Object} request object with a single 'term' property which is the
* input current value
* @param {function} response callback which takes the data to suggest as
* argument
* @param {String} term input current value
*/
_autocompleteSearch(request, response) {
const terms = request.term.toLowerCase().split(/[|,\n]+/);
_autocompleteSearch(term) {
const terms = term.toLowerCase().split(/[|,\n]+/);
const limit = 30;
const sortLimit = 7;
// `this.state.industries` is already sorted by hit count (from IAP).
@@ -178,52 +176,17 @@ class DescriptionScreen extends Component {
.slice(0, limit)
.sort((x, y) => x.hitCountOrder - y.hitCountOrder);
}
this.labelToId = {};
let labels;
this.autocompleteHasResults = !!matches.length;
if (this.autocompleteHasResults) {
if (matches.length <= sortLimit) {
// Sort results by ascending label if few of them.
matches.sort((x, y) => x.label < y.label ? -1 : x.label > y.label ? 1 : 0);
}
labels = matches.map(val => val.label);
matches.forEach(r => {
this.labelToId[r.label] = r.id;
});
} else {
labels = [request.term];
if (matches.length <= sortLimit) {
// Sort results by ascending label if few of them.
matches = matches.sort((x, y) => (x.label < y.label ? -1 : x.label > y.label ? 1 : 0));
}
response(labels);
}
/**
* Called when a menu option is selected. Update the selected industry.
*
* @private
* @param {Event} ev
* @param {Object} ui an object with label and value properties for
* the selected option.
*/
_selectIndustry(ev, ui) {
this._setSelectedIndustry(ui.item.label);
}
/**
* Called on industrySelection input blur. Updates the selected industry.
*
* @private
* @param {Event} ev
*/
_blurIndustrySelection(ev) {
// Check if the input is known by the server.
this.autocompleteHasResults = !!this.labelToId[ev.target.value];
this._setSelectedIndustry(ev.target.value);
return matches.length ? matches : [{ label: term, id: -1 }];
}
selectWebsiteType(id) {
this.state.selectWebsiteType(id);
setTimeout(() => {
this.industrySelection.el.focus();
this.industrySelection.el.querySelector("input").focus();
});
this.checkDescriptionCompletion();
}
@@ -238,7 +201,7 @@ class DescriptionScreen extends Component {
if (selectedType && selectedPurpose && selectedIndustry) {
// If the industry name is not known by the server, send it to the
// IAP server.
if (!this.autocompleteHasResults) {
if (selectedIndustry.id === -1) {
this.orm.call('website', 'configurator_missing_industry', [], {
'unknown_industry': selectedIndustry.label,
});
@@ -249,7 +212,7 @@ class DescriptionScreen extends Component {
}
Object.assign(DescriptionScreen, {
components: {SkipButton},
components: { SkipButton, AutoComplete: IndustrySelectionAutoComplete },
template: 'website.Configurator.DescriptionScreen',
});
@@ -166,8 +166,10 @@
.dropdown-menu, .custom-ui-autocomplete {
@include border-bottom-radius($border-radius);
font-size: inherit;
min-width: 100%;
overflow-y: auto;
&:not(.o_configurator_industry_dropdown) {
min-width: 100%;
}
}
.dropdown-item, .fa-angle-down, .ui-menu-item a, .ui-menu-item a.ui-state-active {
@@ -51,8 +51,12 @@
<span t-attf-class="me-2 {{!state.selectedType ? 'o_configurator_hide' : 'o_configurator_show'}}"> for my</span>
</div>
<div t-attf-class="o_configurator_typing_text d-inline d-md-flex align-items-center o_configurator_industry mb-md-2 mb-lg-4 {{!state.selectedType ? 'o_configurator_hide' : 'o_configurator_show'}}">
<label class="o_configurator_industry_wrapper me-2">
<input t-on-blur="_blurIndustrySelection" t-ref="industrySelection"/>
<label class="o_configurator_industry_wrapper me-2" t-ref="industrySelection">
<AutoComplete
value="state.selectedIndustry?.label ?? ''"
sources="sources"
onSelect.bind="_setSelectedIndustry"
/>
</label>
<span> business</span>
<span t-att-class="!state.selectedIndustry ? 'o_configurator_hide' : 'o_configurator_show'">,</span>