Skip to Content
OWL data_display

Kanban Header

Odoo 19 OWL component — Kanban Header (views)

Live preview Interactive
Source excerpt web/static/src/views/kanban/kanban_header.js
import { Component, useRef } from "@odoo/owl";
import { Dropdown } from "@web/core/dropdown/dropdown";
import { DropdownItem } from "@web/core/dropdown/dropdown_item";
import { _t } from "@web/core/l10n/translation";
import { usePopover } from "@web/core/popover/popover_hook";
import { registry } from "@web/core/registry";
import { utils } from "@web/core/ui/ui_service";
import { memoize } from "@web/core/utils/functions";
import { useService } from "@web/core/utils/hooks";
import { useDebounced } from "@web/core/utils/timing";
import { ColumnProgress } from "@web/views/view_components/column_progress";
import { GroupConfigMenu } from "@web/views/view_components/group_config_menu";

class KanbanHeaderTooltip extends Component {
    static template = "web.KanbanGroupTooltip";
    static props = {
        tooltip: Array,
        close: Function,
    };
}

export class KanbanHeader extends Component {
    static template = "web.KanbanHeader";
    static components = { ColumnProgress, Dropdown, DropdownItem, GroupConfigMenu };
    static props = {
        activeActions: { type: Object },
        canQuickCreate: { type: Boolean },
        deleteGroup: { type: Function },
        dialogClose: { type: Array },
        group: { type: Object },
        list: { type: Object },
        quickCreateState: { type: Object },
        scrollTop: { type: Function },
        tooltipInfo: { type: Object },
        progressBarState: { type: true, optional: true },
    };

    setup() {
        this.dialog = useService("dialog");
        this.orm = useService("orm");
        this.rootRef = useRef("root");
        this.popover = usePopover(KanbanHeaderTooltip);
        this.onTitleMouseEnter = useDebounced(this.onTitleMouseEnter, 400);
    }

    async onTitleMouseEnter(ev) {
        if (!this.hasTooltip) {
            return;
        }
        const tooltip = await this.loadTooltip();
        if (tooltip.length) {
            this.popover.open(ev.target, { tooltip });
        }
    }

    onTitleMouseLeave() {
        this.onTitleMouseEnter.cancel();
        this.popover.close();
    }

    // ------------------------------------------------------------------------
    // Getters
    // ------------------------------------------------------------------------

    get configMenuProps() {
        return {
            activeActions: this.props.activeActions,
            configItems: [
                [
                    "toggle_group",
                    {
                        label: _t("Fold"),
                        method: () => this.group.toggle(),
                        isVisible: () => !utils.isSmall(),
                        class: () => ({
                            o_kanban_toggle_fold: true,
                            disabled: this.props.list.model.useSampleModel,
                        }),
                        icon: "fa-compress",
                    },
                ],
                ...registry.category("group_config_items").getEntries(),
            ],
            deleteGroup: this.props.deleteGroup,
            dialogClose: this.props.dialogClose,
            group: this.props.group,
            list: this.props.list,
        };
    }

    get progressBar() {
        return this.props.progressBarState?.getGroupInfo(this.group);
    }

    get group() {
        return this.props.group;
    }

    get groupAggregate() {
        const { group, progressBarState } = this.props;
        const { sumField } = progressBarState.progressAttributes;
        return progressBarState.getAggregateValue(group, sumField);
    }

    // ------------------------------------------------------------------------
    // Tooltip methods
    // ------------------------------------------------------------------------

    get hasTooltip() {
        const { name, type } = this.group.groupByField;
        return type === "many2one" && this.group.value && name in this.props.tooltipInfo;
    }

    loadTooltip = memoize(async () => {
        const { name, relation: resModel } = this.group.groupByField;
        const tooltipInfo = this.props.tooltipInfo[name];
        const fieldNames = Object.keys(tooltipInfo);
        const [values] = await this.orm.silent.read(
            resModel,
            [this.group.value],
Registry / API
Registry name
KanbanHeader
Category
Module
web
Slug
kanban-header
Nav group
data_display