[IMP] web: SelectMenu has multiSelect props

This commit adds the ability to select multiple values from the
SelectMenu component. A list of tags are displayed in the toggler
to remove already selected values, and selected values are colored
in the list of choices.

This feature is similar to how jquery select2 handles multiple
selections, by adding the values in the input toggler. The feature
will be used by the studio sidebar during the refactor.

Part-of: odoo/odoo#115799
This commit is contained in:
luvi
2023-04-03 13:55:17 +02:00
committed by Lucas Perais
parent d760b81271
commit 2d28239f9b
4 changed files with 53 additions and 10 deletions
@@ -8,16 +8,18 @@ import { shallowEqual } from "@web/core/utils/objects";
import { useDebounced } from "@web/core/utils/timing";
import { scrollTo } from "@web/core/utils/scrolling";
import { fuzzyLookup } from "@web/core/utils/search";
import { TagsList } from "@web/views/fields/many2many_tags/tags_list";
export class SelectMenu extends Component {
static template = "web.SelectMenu";
static components = { Dropdown, DropdownItem };
static components = { Dropdown, DropdownItem, TagsList };
static defaultProps = {
value: undefined,
class: "",
togglerClass: "",
multiSelect: false,
onSelect: () => {},
required: false,
searchable: true,
@@ -64,6 +66,7 @@ export class SelectMenu extends Component {
searchable: { type: Boolean, optional: true },
searchPlaceholder: { type: String, optional: true },
value: { optional: true },
multiSelect: { type: Boolean, optional: true },
onSelect: { type: Function, optional: true },
slots: { type: Object, optional: true },
};
@@ -101,6 +104,24 @@ export class SelectMenu extends Component {
return "";
}
get multiSelectChoices() {
const choices = [
...this.props.choices,
...this.props.groups.flatMap((g) => g.choices),
].filter((c) => this.props.value.includes(c.value));
return choices.map((c) => {
return {
id: c.value,
text: c.label,
onDelete: () => {
const values = [...this.props.value];
values.splice(values.indexOf(c.value), 1);
this.props.onSelect(values);
},
};
});
}
onOpened() {
// Using useAutofocus inside the dropdown does not
// work properly so we set the focus manually.
@@ -115,6 +136,9 @@ export class SelectMenu extends Component {
}
isOptionSelected(choice) {
if (this.props.multiSelect) {
return this.props.value.includes(choice.value);
}
return this.props.value === choice.value;
}
@@ -214,6 +238,14 @@ export class SelectMenu extends Component {
return choices.flatMap((choice, index) => (index === 0 ? 0 : choice.isGroup ? index : []));
}
onItemSelected(value) {
if (this.props.multiSelect) {
this.props.onSelect([...this.props.value, value]);
} else {
this.props.onSelect(value);
}
}
// ==========================================================================================
// # Scroll #
// ==========================================================================================
@@ -23,6 +23,10 @@
top: 40px;
}
}
.o_tag {
margin: 2px;
}
}
.dropup .o_select_menu_menu {
@@ -12,13 +12,20 @@
onScroll.bind="onScroll"
>
<t t-set-slot="toggler">
<span class="o_select_menu_toggler_slot text-start">
<t t-if="!props.slots or !props.slots.default" t-esc="displayValue" />
<t t-else="" t-slot="default" />
</span>
<span t-if="canClear()" t-on-click.stop="() => this.props.onSelect(null)" class="o_select_menu_toggler_clear p-0 m-0">
<i class="fa fa-times"></i>
</span>
<t t-if="props.multiSelect">
<div class="text-wrap text-start">
<TagsList tags="multiSelectChoices"/>
</div>
</t>
<t t-else="">
<span class="o_select_menu_toggler_slot text-start text-truncate">
<t t-if="!props.slots or !props.slots.default" t-esc="displayValue" />
<t t-else="" t-slot="default" />
</span>
<span t-if="canClear()" t-on-click.stop="() => this.props.onSelect(null)" class="o_select_menu_toggler_clear p-0 m-0">
<i class="fa fa-times"></i>
</span>
</t>
<span class="o_select_menu_toggler_caret p-0 m-0">
<i class="fa fa-caret-down"></i>
</span>
@@ -45,7 +52,7 @@
</div>
<DropdownItem
t-if="!choice.isGroup"
onSelected="() => this.props.onSelect(choice.value)"
onSelected="() => this.onItemSelected(choice.value)"
class="getItemClass(choice)"
>
<t t-if="props.slots and props.slots.choice" t-slot="choice" data="choice"/>
@@ -15,7 +15,7 @@ import { CalendarQuickCreate } from "./quick_create/calendar_quick_create";
import { Component, useState } from "@odoo/owl";
const SCALE_LABELS = {
export const SCALE_LABELS = {
day: _lt("Day"),
week: _lt("Week"),
month: _lt("Month"),