OWL
forms
Status Bar Field
Odoo 19 OWL component — Status Bar Field (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
StatusBarField- Category
—- Module
web- Slug
status-bar-field- Nav group
forms