Skip to Content
OWL data_display

Action Menus

Odoo 19 OWL component — Action Menus (search)

Live preview Interactive
Source excerpt web/static/src/search/action_menus/action_menus.js
import { browser } from "@web/core/browser/browser";
import { makeContext } from "@web/core/context";
import { session } from "@web/session";
import { Dropdown } from "@web/core/dropdown/dropdown";
import { DropdownItem } from "@web/core/dropdown/dropdown_item";
import { _t } from "@web/core/l10n/translation";
import { useService } from "@web/core/utils/hooks";

import { Component, onWillStart, onWillUpdateProps, useState } 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')
 *
 * The side bar is the group of dropdown menus located on the left side of the
 * control panel. Its role is to display a list of items depending on the view
 * 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
 */
export class ActionMenus extends Component {
    static template = "web.ActionMenus";
    static components = {
        Dropdown,
        DropdownItem,
    };
    static props = {
        getActiveIds: Function,
        context: Object,
        resModel: String,
        printDropdownTitle: { type: String, optional: true },
        domain: { type: Array, optional: true },
        isDomainSelected: { type: Boolean, optional: true },
        items: {
            type: Object,
            shape: {
                action: { type: Array, optional: true },
                print: { type: Array, optional: true },
            },
        },
        onActionExecuted: { type: Function, optional: true },
        shouldExecuteAction: { type: Function, optional: true },
        loadExtraPrintItems: { type: Function, optional: true },
    };
    static defaultProps = {
        printDropdownTitle: _t("Print"),
        onActionExecuted: () => {},
        shouldExecuteAction: () => true,
        loadExtraPrintItems: () => [],
    };

    setup() {
        this.orm = useService("orm");
        this.actionService = useService("action");
        this.state = useState({ printItems: []})
        onWillStart(async () => {
            this.actionItems = await this.getActionItems(this.props);
        });
        onWillUpdateProps(async (nextProps) => {
            this.actionItems = await this.getActionItems(nextProps);
        });
    }

    //---------------------------------------------------------------------
    // Private
    //---------------------------------------------------------------------

    async getActionItems(props) {
        return (props.items.action || []).map((action) => {
            if (action.callback) {
                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,
                };
            }
        });
    }

    //---------------------------------------------------------------------
    // Handlers
    //---------------------------------------------------------------------

    async executeAction(action) {
        let activeIds = this.props.getActiveIds();
        if (this.props.isDomainSelected) {
            activeIds = await this.orm.search(this.props.resModel, this.props.domain, {
                limit: session.active_ids_limit,
                context: this.props.context,
            });
        }
        const activeIdsContext = {
            active_id: activeIds[0],
            active_ids: activeIds,
            active_model: this.props.resModel,
        };
        if (this.props.domain) {
            // keep active_domain in context for backward compatibility
            // reasons, and to allow actions to bypass the active_ids_limit
            activeIdsContext.active_domain = this.props.domain;
        }
        const context = makeContext([this.props.context, activeIdsContext]);
        return this.actionService.doAction(action.id, {
            additionalContext: context,
            onClose: this.props.onActionExecuted,
        });
    }

    /**
     * Handler used to determine which way must be used to execute a selected
     * action: it will be either:
Registry / API
Registry name
ActionMenus
Category
Module
web
Slug
action-menus
Nav group
data_display