diff --git a/addons/web/static/src/views/fields/many2many_tags/many2many_tags_field.js b/addons/web/static/src/views/fields/many2many_tags/many2many_tags_field.js index 08faa353ce3..70a8e4662ae 100644 --- a/addons/web/static/src/views/fields/many2many_tags/many2many_tags_field.js +++ b/addons/web/static/src/views/fields/many2many_tags/many2many_tags_field.js @@ -79,27 +79,23 @@ export class Many2ManyTagsField extends Component { get evalContext() { return this.props.record.evalContext; } - get canEditColor() { - return this.props.canEditColor && this.props.record.viewType !== "list"; - } get string() { return this.props.record.activeFields[this.props.name].string; } - get tags() { - return this.props.value.records.map((record) => ({ + getTagProps(record) { + return { id: record.id, // datapoint_X resId: record.resId, text: record.data.display_name, colorIndex: record.data[this.props.colorField], - onClick: (ev) => this.onBadgeClick(ev, record), onDelete: !this.props.readonly ? () => this.deleteTag(record.id) : undefined, onKeydown: this.onTagKeydown.bind(this), - })); + }; } - get canOpenColorDropdown() { - return this.handlesColor() && this.canEditColor; + get tags() { + return this.props.value.records.map((record) => this.getTagProps(record)); } get showM2OSelectionField() { @@ -112,34 +108,6 @@ export class Many2ManyTagsField extends Component { this.props.value.replaceWith(ids); } - handlesColor() { - return this.props.colorField !== undefined && this.props.colorField !== null; - } - - switchTagColor(colorIndex, tag) { - const tagRecord = this.props.value.records.find((record) => record.id === tag.id); - tagRecord.update({ [this.props.colorField]: colorIndex }); - tagRecord.save(); - this.closePopover(); - } - - onTagVisibilityChange(isHidden, tag) { - const tagRecord = this.props.value.records.find((record) => record.id === tag.id); - if (tagRecord.data[this.props.colorField] != 0) { - this.previousColorsMap[tagRecord.resId] = tagRecord.data[this.props.colorField]; - } - tagRecord.update({ - [this.props.colorField]: isHidden ? 0 : this.previousColorsMap[tagRecord.resId] || 1, - }); - tagRecord.save(); - this.closePopover(); - } - - closePopover() { - this.popoverCloseFn(); - this.popoverCloseFn = null; - } - getDomain() { return Domain.and([ this.domain, @@ -147,35 +115,6 @@ export class Many2ManyTagsField extends Component { ]).toList(this.context); } - onBadgeClick(ev, record) { - if (!this.canOpenColorDropdown) { - return; - } - const isClosed = !document.querySelector(".o_tag_popover"); - if (isClosed) { - this.currentPopoverEl = null; - } - if (this.popoverCloseFn) { - this.closePopover(); - } - if (isClosed || this.currentPopoverEl !== ev.currentTarget) { - this.currentPopoverEl = ev.currentTarget; - this.popoverCloseFn = this.popover.add( - ev.currentTarget, - this.constructor.components.Popover, - { - colors: this.constructor.RECORD_COLORS, - tag: { - id: record.id, - colorIndex: record.data[this.props.colorField], - }, - switchTagColor: this.switchTagColor.bind(this), - onTagVisibilityChange: this.onTagVisibilityChange.bind(this), - } - ); - } - } - focusTag(index) { const autoCompleteParent = this.autoCompleteRef.el.parentElement; const tags = autoCompleteParent.getElementsByClassName("badge"); @@ -279,14 +218,12 @@ Many2ManyTagsField.SEARCH_MORE_LIMIT = 320; Many2ManyTagsField.template = "web.Many2ManyTagsField"; Many2ManyTagsField.components = { - Popover: Many2ManyTagsFieldColorListPopover, TagsList, Many2XAutocomplete, }; Many2ManyTagsField.props = { ...standardFieldProps, - canEditColor: { type: Boolean, optional: true }, canCreate: { type: Boolean, optional: true }, canQuickCreate: { type: Boolean, optional: true }, canCreateEdit: { type: Boolean, optional: true }, @@ -298,7 +235,6 @@ Many2ManyTagsField.props = { }; Many2ManyTagsField.defaultProps = { canCreate: true, - canEditColor: true, canQuickCreate: true, canCreateEdit: true, nameCreateField: "name", @@ -320,7 +256,6 @@ Many2ManyTagsField.extractProps = ({ attrs, field }) => { return { colorField: attrs.options.color_field, nameCreateField: attrs.options.create_name_field, - canEditColor: !attrs.options.no_edit_color, relation: field.relation, canQuickCreate: canCreate && !noQuickCreate, canCreateEdit: canCreate && !noCreateEdit, @@ -330,5 +265,91 @@ Many2ManyTagsField.extractProps = ({ attrs, field }) => { }; registry.category("fields").add("many2many_tags", Many2ManyTagsField); -registry.category("fields").add("form.many2many_tags", Many2ManyTagsField); -registry.category("fields").add("list.many2many_tags", Many2ManyTagsField); + +/** + * A specialization that allows to edit the color with the colorpicker. + * Used in form view. + */ +export class Many2ManyTagsFieldColorEditable extends Many2ManyTagsField { + getTagProps(record) { + const props = super.getTagProps(record); + props.onClick = (ev) => this.onBadgeClick(ev, record); + return props; + } + + closePopover() { + this.popoverCloseFn(); + this.popoverCloseFn = null; + } + + onBadgeClick(ev, record) { + if (!this.props.canEditColor) { + return; + } + const isClosed = !document.querySelector(".o_tag_popover"); + if (isClosed) { + this.currentPopoverEl = null; + } + if (this.popoverCloseFn) { + this.closePopover(); + } + if (isClosed || this.currentPopoverEl !== ev.currentTarget) { + this.currentPopoverEl = ev.currentTarget; + this.popoverCloseFn = this.popover.add( + ev.currentTarget, + this.constructor.components.Popover, + { + colors: this.constructor.RECORD_COLORS, + tag: { + id: record.id, + colorIndex: record.data[this.props.colorField], + }, + switchTagColor: this.switchTagColor.bind(this), + onTagVisibilityChange: this.onTagVisibilityChange.bind(this), + } + ); + } + } + + onTagVisibilityChange(isHidden, tag) { + const tagRecord = this.props.value.records.find((record) => record.id === tag.id); + if (tagRecord.data[this.props.colorField] != 0) { + this.previousColorsMap[tagRecord.resId] = tagRecord.data[this.props.colorField]; + } + tagRecord.update({ + [this.props.colorField]: isHidden ? 0 : this.previousColorsMap[tagRecord.resId] || 1, + }); + tagRecord.save(); + this.closePopover(); + } + + switchTagColor(colorIndex, tag) { + const tagRecord = this.props.value.records.find((record) => record.id === tag.id); + tagRecord.update({ [this.props.colorField]: colorIndex }); + tagRecord.save(); + this.closePopover(); + } +} + +Many2ManyTagsFieldColorEditable.components = { + ...Many2ManyTagsField.components, + Popover: Many2ManyTagsFieldColorListPopover, +}; +Many2ManyTagsFieldColorEditable.props = { + ...Many2ManyTagsField.props, + canEditColor: { type: Boolean, optional: true }, +}; +Many2ManyTagsFieldColorEditable.defaultProps = { + ...Many2ManyTagsField.defaultProps, + canEditColor: true, +}; +Many2ManyTagsFieldColorEditable.extractProps = (params) => { + const props = Many2ManyTagsField.extractProps(params); + const attrs = params.attrs; + return { + ...props, + canEditColor: !attrs.options.no_edit_color, + }; +}; + +registry.category("fields").add("form.many2many_tags", Many2ManyTagsFieldColorEditable); diff --git a/addons/web/static/tests/views/fields/many2many_tags_field_tests.js b/addons/web/static/tests/views/fields/many2many_tags_field_tests.js index 7f059f78bf8..5133515b477 100644 --- a/addons/web/static/tests/views/fields/many2many_tags_field_tests.js +++ b/addons/web/static/tests/views/fields/many2many_tags_field_tests.js @@ -342,8 +342,53 @@ QUnit.module("Fields", (hooks) => { // click on the tag: should do nothing and open the form view click(target.querySelector(".o_field_many2many_tags .badge :nth-child(1)")); - assert.verifySteps(["selectRecord"]); + await nextTick(); + + assert.containsNone(target, ".o_colorlist"); + + await click(target.querySelectorAll(".o_list_record_selector")[1]); + click(target.querySelector(".o_field_many2many_tags .badge :nth-child(1)")); + assert.verifySteps(["selectRecord"]); + await nextTick(); + + assert.containsNone(target, ".o_colorlist"); + }); + + QUnit.test("Many2ManyTagsField in tree view -- multi edit", async function (assert) { + serverData.models.partner.records[0].timmy = [12, 14]; + + await makeView({ + type: "list", + resModel: "partner", + serverData, + arch: ` + + + + `, + selectRecord: () => { + assert.step("selectRecord"); + }, + }); + + assert.containsN(target, ".o_field_many2many_tags .badge", 2, "there should be 2 tags"); + assert.containsNone(target, ".badge.dropdown-toggle", "the tags should not be dropdowns"); + + // click on the tag: should do nothing and open the form view + click(target.querySelector(".o_field_many2many_tags .badge :nth-child(1)")); + assert.verifySteps(["selectRecord"]); + await nextTick(); + + assert.containsNone(target, ".o_colorlist"); + + await click(target.querySelectorAll(".o_list_record_selector")[1]); + click(target.querySelector(".o_field_many2many_tags .badge :nth-child(1)")); + assert.verifySteps([]); + await nextTick(); + + assert.containsOnce(target, ".o_selected_row"); + assert.containsNone(target, ".o_colorlist"); }); QUnit.test("Many2ManyTagsField view a domain", async function (assert) {