Skip to Content
fields forms

Statusbar

Odoo 19 fields — Statusbar (views)

Live preview Interactive
Source excerpt web/static/src/views/fields/statusbar/statusbar_field.js
import { Component, onWillRender, useEffect, useExternalListener, useRef } from "@odoo/owl";
import { useCommand } from "@web/core/commands/command_hook";
import { Domain } from "@web/core/domain";
import { Dropdown } from "@web/core/dropdown/dropdown";
import { DropdownItem } from "@web/core/dropdown/dropdown_item";
import { _t } from "@web/core/l10n/translation";
import { registry } from "@web/core/registry";
import { groupBy } from "@web/core/utils/arrays";
import { throttleForAnimation } from "@web/core/utils/timing";
import { getFieldDomain } from "@web/model/relational_model/utils";
import { useSpecialData } from "@web/views/fields/relational_utils";
import { standardFieldProps } from "../standard_field_props";

/**
 * @typedef {import("../standard_field_props").StandardFieldProps & {
 *  domain?: [Array, Function];
 *  foldField?: string;
 *  isDisabled?: boolean;
 *  visibleSelection?: string[];
 *  withCommand?: boolean;
 * }} StatusBarFieldProps
 *
 * @typedef StatusBarItem
 * @property {number} value
 * @property {string} label
 * @property {boolean} isFolded
 * @property {boolean} isSelected
 *
 * @typedef StatusBarList
 * @property {string} label
 * @property {StatusBarItem[]} items
 */

/**
 * @param {...HTMLElement} els
 */
const hide = (...els) => els.forEach((el) => el.classList.add("d-none"));

/**
 * @param {...HTMLElement} els
 */
const show = (...els) => els.forEach((el) => el.classList.remove("d-none"));

/** @extends {Component<StatusBarFieldProps>} */
export class StatusBarField extends Component {
    static template = "web.StatusBarField";
    static components = {
        Dropdown,
        DropdownItem,
    };
    static props = {
        ...standardFieldProps,
        domain: { type: [Array, Function], optional: true },
        foldField: { type: String, optional: true },
        isDisabled: { type: Boolean, optional: true },
        visibleSelection: { type: Array, element: String, optional: true },
        withCommand: { type: Boolean, optional: true },
    };

    setup() {
        // Properties
        this.items = {};
        this.beforeRef = useRef("before");
        this.rootRef = useRef("root");
        this.afterRef = useRef("after");
        this.dropdownRef = useRef("dropdown");

        // Resize listeners
        let status = "idle";
        const adjust = () => {
            status = "adjusting";
            this.adjustVisibleItems();
            this.render();
        };

        useEffect(() => {
            if (status === "shouldAdjust") {
                adjust();
            }
        });

        let forceRecomputeItems = false;
        onWillRender(() => {
            if (status !== "adjusting" || forceRecomputeItems) {
                Object.assign(this.items, this.getSortedItems());
                status = "shouldAdjust";
            } else {
                status = "idle";
            }
            forceRecomputeItems = false;
        });

        useExternalListener(window, "resize", throttleForAnimation(adjust));

        // Special data
        if (this.field.type === "many2one") {
            this.specialData = useSpecialData(async (orm, props) => {
                const { foldField, name: fieldName, record } = props;
                const { relation } = record.fields[fieldName];
                const fieldNames = this.getFieldNames(props);
                if (foldField) {
                    fieldNames.push(foldField);
                }
                const value = record.data[fieldName];
                let domain = getFieldDomain(record, fieldName, props.domain);
                domain = Domain.and([this.getDomain(props), domain]).toList();
                if (domain.length && value) {
                    domain = Domain.or([[["id", "=", value.id]], domain]).toList(
                        record.evalContext
                    );
                }
                const res = orm.searchRead(relation, domain, fieldNames);
                forceRecomputeItems = true;
                return res;
            });
        }

        // Command palette
        if (this.props.withCommand) {
            const moveToCommandName = _t("Move to %s...", this.field.string);
Registry / API
Registry name
statusbar
Category
fields
Module
web
Slug
statusbar
Nav group
forms