Skip to content

Commit

Permalink
[FIX] web, *: cogMenu visual flaws
Browse files Browse the repository at this point in the history
*: account, base_import, board, website

This PR enhances the "cog menu" by fixing several UX flaws.
Notable improvements include:
- Reordering entries in a more logical manner, enhancing user intuitiveness.
- Assigning icons to common actions for quick comprehension.
- Grouping both print actions and module-specific actions for better organization.

Enterprise:
- odoo/enterprise#41851

task-3337951
task-3355224 (milk post-merge fixes)
part of task-3326263

closes odoo#124413

X-original-commit: 5967728
Related: odoo/enterprise#42212
Signed-off-by: Pierre Paridans (app) <[email protected]>
Co-authored-by: Brieuc-brd <[email protected]>
Co-authored-by: Pierre Paridans <[email protected]>
Co-authored-by: stefanorigano (SRI) <[email protected]>
  • Loading branch information
3 people committed Jun 9, 2023
1 parent 42f28ec commit 25f43c2
Show file tree
Hide file tree
Showing 24 changed files with 204 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
}, {
trigger: '.o_control_panel .o_cp_action_menus .dropdown-toggle',
extra_trigger: '.o_breadcrumb .active:contains("INV/")',
}, {
trigger: `.o_control_panel .o_cp_action_menus .dropdown-toggle:contains("${_t("Print")}")`,
run: function () {
this.$anchor[0].dispatchEvent(new MouseEvent("mouseenter"));
},
}, {
trigger: '.o_control_panel .o_cp_action_menus .o_menu_item:contains("' + _t('Invoices without Payment') + '")',
}, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { DropdownItem } from "@web/core/dropdown/dropdown_item";
import { registry } from "@web/core/registry";
import { useService } from "@web/core/utils/hooks";
import { Component } from "@odoo/owl";
import { STATIC_ACTIONS_GROUP_NUMBER } from "@web/search/action_menus/action_menus";

const cogMenuRegistry = registry.category("cogMenu");

Expand Down Expand Up @@ -37,7 +38,7 @@ export class ImportRecords extends Component {

export const importRecordsItem = {
Component: ImportRecords,
groupNumber: 4,
groupNumber: STATIC_ACTIONS_GROUP_NUMBER,
isDisplayed: ({ config, isSmall }) =>
!isSmall &&
config.actionType === "ir.actions.act_window" &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<t t-name="base_import.ImportRecords" owl="1">
<DropdownItem class="'o_import_menu'" onSelected.bind="importRecords">
Import records
<i class="fa fa-fw fa-download me-1"/>Import records
</DropdownItem>
</t>

Expand Down
9 changes: 3 additions & 6 deletions addons/base_import/static/tests/import_records_tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@ QUnit.module("Base Import Tests", (hooks) => {
},
});

await toggleActionMenu(target);
assert.containsOnce(target, ".o_cp_action_menus .o-dropdown--menu");
assert.containsNone(target, ".o_cp_action_menus");
assert.containsNone(target, ".o_import_menu");
}
);
Expand All @@ -95,8 +94,7 @@ QUnit.module("Base Import Tests", (hooks) => {
},
});

await toggleActionMenu(target);
assert.containsOnce(target, ".o_cp_action_menus .o-dropdown--menu");
assert.containsNone(target, ".o_cp_action_menus");
assert.containsNone(target, ".o_import_menu");
}
);
Expand Down Expand Up @@ -239,8 +237,7 @@ QUnit.module("Base Import Tests", (hooks) => {

await selectDropdownItem(target, "m2o", "Search More...");
const dialog = target.querySelector(".modal");
await toggleActionMenu(dialog);
assert.containsOnce(dialog, ".o_cp_action_menus .o-dropdown--menu");
assert.containsNone(dialog, ".o_cp_action_menus");
assert.containsNone(dialog, ".o_import_menu");
}
);
Expand Down
2 changes: 1 addition & 1 deletion addons/board/static/src/add_to_board/add_to_board.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ AddToBoard.components = { Dropdown };

export const addToBoardItem = {
Component: AddToBoard,
groupNumber: 4,
groupNumber: 20,
isDisplayed: ({ config }) => config.actionType === "ir.actions.act_window" && config.actionId,
};

Expand Down
12 changes: 7 additions & 5 deletions addons/board/static/src/add_to_board/add_to_board.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@

<t t-name="board.AddToBoard" owl="1">
<Dropdown class="'o_add_to_board'">
<t t-set-slot="toggler">Add to my dashboard</t>
<div class="px-3 py-2">
<input type="text" class="o_input" t-ref="autofocus" t-model.trim="state.name" t-on-keydown="onInputKeydown" />
</div>
<div class="px-3 py-2">
<t t-set-slot="toggler">
<img src="/board/static/description/icon.svg" alt="Dashboard Icon" class="o_cp_action_app_icon oi-large me-1"/>
Dashboard
</t>
<div class="px-3 pb-2">
<label class="mb-2">Add to my dashboard</label>
<input type="text" class="o_input mb-3" t-ref="autofocus" t-model.trim="state.name" t-on-keydown="onInputKeydown" />
<button type="button" class="btn btn-primary" t-on-click="addToBoard">Add</button>
</div>
</Dropdown>
Expand Down
10 changes: 9 additions & 1 deletion addons/web/static/src/search/action_menus/action_menus.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import { DropdownItem } from "@web/core/dropdown/dropdown_item";
import { useService } from "@web/core/utils/hooks";

import { Component, onWillStart, onWillUpdateProps } from "@odoo/owl";

export const STATIC_ACTIONS_GROUP_NUMBER = 1;
export const ACTIONS_GROUP_NUMBER = 100;

/**
* Action menus (or Action/Print bar, previously called 'Sidebar')
*
Expand Down Expand Up @@ -46,12 +50,16 @@ export class ActionMenus extends Component {
async getActionItems(props) {
return (props.items.action || []).map((action) => {
if (action.callback) {
return Object.assign({ key: `action-${action.description}` }, action);
return Object.assign(
{ key: `action-${action.description}`, groupNumber: ACTIONS_GROUP_NUMBER },
action
);
} else {
return {
action,
description: action.name,
key: action.id,
groupNumber: action.groupNumber || ACTIONS_GROUP_NUMBER,
};
}
});
Expand Down
3 changes: 2 additions & 1 deletion addons/web/static/src/search/action_menus/action_menus.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@
<Dropdown t-if="actionItems.length" class="'d-inline-block'" togglerClass="'btn btn-secondary'" hotkey="'u'">
<t t-set-slot="toggler">
<i class="me-md-1 fa fa-cog"/>
<span class="o_dropdown_title">Action</span>
<span class="o_dropdown_title">Actions</span>
</t>
<t t-foreach="actionItems" t-as="item" t-key="item.key">
<t t-if="item.Component" t-component="item.Component" t-props="item.props" />
<DropdownItem t-else="" class="'o_menu_item'" onSelected="() => this.onItemSelected(item)">
<i t-if="item.icon" t-att-class="item.icon + ' me-1 fa-fw oi-fw'"/>
<t t-esc="item.description"/>
</DropdownItem>
</t>
Expand Down
20 changes: 0 additions & 20 deletions addons/web/static/src/search/cog_menu/action_menus_items.js

This file was deleted.

30 changes: 0 additions & 30 deletions addons/web/static/src/search/cog_menu/action_menus_items.xml

This file was deleted.

50 changes: 40 additions & 10 deletions addons/web/static/src/search/cog_menu/cog_menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

import { registry } from "@web/core/registry";
import { Dropdown } from "@web/core/dropdown/dropdown";
import { ActionMenus } from "@web/search/action_menus/action_menus";

import { Component } from "@odoo/owl";
import { onWillStart, onWillUpdateProps } from "@odoo/owl";

const cogMenuRegistry = registry.category("cogMenu");

Expand All @@ -17,32 +18,61 @@ const cogMenuRegistry = registry.category("cogMenu");
* type and selected records and to execute a set of actions on active records.
* It is made out of 2 dropdown: Print and Action.
*
* @extends Component
* @extends ActionMenus
*/
export class CogMenu extends Component {
export class CogMenu extends ActionMenus {
static template = "web.CogMenu";
static components = {
...ActionMenus.components,
Dropdown,
};
static props = {
slots: { type: Object, optional: true },
...ActionMenus.props,
getActiveIds: { type: ActionMenus.props.getActiveIds, optional: true },
context: { type: ActionMenus.props.context, optional: true },
resModel: { type: ActionMenus.props.resModel, optional: true },
items: { ...ActionMenus.props.items, optional: true },
};
static defaultProps = {
...ActionMenus.defaultProps,
items: {},
};

setup() {
super.setup();
onWillStart(async () => {
this.registryItems = await this._registryItems();
});
onWillUpdateProps(async () => {
this.registryItems = await this._registryItems();
});
}

get hasItems() {
return this.cogItems.length || !!this.props.slots?.default;
return this.cogItems.length || this.printItems.length;
}

get cogItems() {
const registryMenus = [];
async _registryItems() {
const items = [];
for (const item of cogMenuRegistry.getAll()) {
if ("isDisplayed" in item ? item.isDisplayed(this.env) : true) {
registryMenus.push({
if ("isDisplayed" in item ? await item.isDisplayed(this.env) : true) {
items.push({
Component: item.Component,
groupNumber: item.groupNumber,
key: item.Component.name,
});
}
}
return registryMenus;
return items;
}

get cogItems() {
return [...this.actionItems, ...this.registryItems].sort((item1, item2) => {
const grp = (item1.groupNumber || 0) - (item2.groupNumber || 0);
if (grp !== 0) {
return grp;
}
return (item1.sequence || 0) - (item2.sequence || 0);
});
}
}
12 changes: 12 additions & 0 deletions addons/web/static/src/search/cog_menu/cog_menu.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.o_cp_action_menus {
.o_cp_action_app_icon {
width: var(--oi-font-size, 1em);
vertical-align: -0.2em;
}

.dropdown-item:not(.focus) > .o_cp_action_app_icon {
// Temporary workaround until we'll support inline SVGs icons
// globally (eg. Spreadsheet's toolbar icons)
filter: saturate(0%) brightness(0%) invert(23%) sepia(7%) saturate(1896%) hue-rotate(179deg) brightness(91%) contrast(88%);
}
}
34 changes: 31 additions & 3 deletions addons/web/static/src/search/cog_menu/cog_menu.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,42 @@
<t t-set-slot="toggler">
<i class="fa fa-cog"/>
</t>
<t t-foreach="cogItems" t-as="item" t-key="item.id or item.key">

<t t-if="printItems.length">
<Dropdown t-if="printItems.length > 1">
<t t-set-slot="toggler">
<i class="fa fa-print me-1"/> Print
</t>
<DropdownItem
t-foreach="printItems"
t-as="item"
t-key="item.key"
class="'o_menu_item'"
onSelected="() => this.onItemSelected(item)"
>
<t t-esc="item.description"/>
</DropdownItem>
</Dropdown>

<DropdownItem t-else="" class="'o_menu_item'" onSelected="() => this.onItemSelected(printItems[0])">
<i class="fa fa-print me-1"/> <t t-out="printItems[0].description"/>
</DropdownItem>
</t>

<t t-foreach="cogItems" t-as="item" t-key="item.key">
<t t-if="currentGroup !== null and currentGroup !== item.groupNumber">
<div role="separator" class="dropdown-divider"/>
</t>
<t t-component="item.Component"/>

<t t-if="item.Component" t-component="item.Component" t-props="item.props"/>

<DropdownItem t-else="" class="'o_menu_item'" onSelected="() => this.onItemSelected(item)">
<i t-if="item.icon" t-att-class="item.icon" class="fa-fw oi-fw me-1"/>
<t t-esc="item.description"/>
</DropdownItem>

<t t-set="currentGroup" t-value="item.groupNumber"/>
</t>
<t t-slot="default"/>
</Dropdown>
</div>
</t>
Expand Down
13 changes: 10 additions & 3 deletions addons/web/static/src/views/form/form_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { ButtonBox } from "./button_box/button_box";
import { ViewButton } from "@web/views/view_button/view_button";
import { Field } from "@web/views/fields/field";
import { CogMenu } from "@web/search/cog_menu/cog_menu";
import { ActionMenusItems } from "@web/search/cog_menu/action_menus_items";
import { STATIC_ACTIONS_GROUP_NUMBER } from "@web/search/action_menus/action_menus";

import { Component, onRendered, useEffect, useRef } from "@odoo/owl";
import { useViewCompiler } from "../view_compiler";
Expand Down Expand Up @@ -327,6 +327,7 @@ export class FormController extends Component {
isAvailable: () => this.archiveEnabled && this.model.root.isActive,
sequence: 10,
description: this.env._t("Archive"),
icon: "oi oi-archive",
callback: () => {
const dialogProps = {
body: this.env._t("Are you sure that you want to archive this record?"),
Expand All @@ -340,18 +341,21 @@ export class FormController extends Component {
unarchive: {
isAvailable: () => this.archiveEnabled && !this.model.root.isActive,
sequence: 20,
icon: "oi oi-unarchive",
description: this.env._t("Unarchive"),
callback: () => this.model.root.unarchive(),
},
duplicate: {
isAvailable: () => activeActions.create && activeActions.duplicate,
sequence: 30,
icon: "fa fa-clone",
description: this.env._t("Duplicate"),
callback: () => this.duplicateRecord(),
},
delete: {
isAvailable: () => activeActions.delete && !this.model.root.isNew,
sequence: 40,
icon: "fa fa-trash-o",
description: this.env._t("Delete"),
callback: () => this.deleteRecord(),
skipSave: true,
Expand All @@ -364,7 +368,11 @@ export class FormController extends Component {
const staticActionItems = Object.entries(this.getStaticActionMenuItems())
.filter(([key, item]) => item.isAvailable === undefined || item.isAvailable())
.sort(([k1, item1], [k2, item2]) => (item1.sequence || 0) - (item2.sequence || 0))
.map(([key, item]) => Object.assign({ key }, omit(item, "isAvailable", "sequence")));
.map(([key, item]) =>
Object.assign({ key }, omit(item, "isAvailable", "sequence"), {
groupNumber: STATIC_ACTIONS_GROUP_NUMBER,
})
);

return {
action: [...staticActionItems, ...(actionMenus.action || [])],
Expand Down Expand Up @@ -511,7 +519,6 @@ FormController.components = {
ViewButton,
Field,
CogMenu,
ActionMenusItems,
};
FormController.props = {
...standardViewProps,
Expand Down
Loading

0 comments on commit 25f43c2

Please sign in to comment.