[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:
@@ -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"),
|
||||
|
||||
Reference in New Issue
Block a user