OWL
overlay
Calendar Common Popover
Odoo 19 OWL component — Calendar Common Popover (views)
Live preview
Interactive
Source excerpt
web/static/src/views/calendar/calendar_common/calendar_common_popover.js
import { _t } from "@web/core/l10n/translation";
import { Dialog } from "@web/core/dialog/dialog";
import { evaluateBooleanExpr } from "@web/core/py_js/py";
import { is24HourFormat } from "@web/core/l10n/time";
import { registry } from "@web/core/registry";
import { Field } from "@web/views/fields/field";
import { Record } from "@web/model/record";
import { getFormattedDateSpan } from "@web/views/calendar/utils";
import { Component, useExternalListener } from "@odoo/owl";
export class CalendarCommonPopover extends Component {
static template = "web.CalendarCommonPopover";
static subTemplates = {
popover: "web.CalendarCommonPopover.popover",
body: "web.CalendarCommonPopover.body",
footer: "web.CalendarCommonPopover.footer",
};
static components = {
Dialog,
Field,
Record,
};
static props = {
close: Function,
record: Object,
model: Object,
createRecord: Function,
deleteRecord: Function,
editRecord: Function,
};
setup() {
this.time = null;
this.timeDuration = null;
this.date = null;
this.dateDuration = null;
useExternalListener(window, "pointerdown", (e) => {
// Prevent the default behavior so the pointer down event only triggers the click-away callback (closing the popover).
// If the clicked element is the popover target, allow the default click and drag-&-drop events,
// which will also close the popover.
if (!e.target.closest(`.fc-event[data-event-id="${this.props.record.id}"]`)) {
e.preventDefault();
}
}, { capture: true });
this.computeDateTimeAndDuration();
}
get activeFields() {
return this.props.model.activeFields;
}
get isEventEditable() {
return this.props.model.canEdit;
}
get isEventDeletable() {
return this.props.model.canDelete;
}
get isEventViewable() {
return true;
}
get hasFooter() {
return this.isEventEditable || this.isEventDeletable || this.isEventViewable;
}
isInvisible(fieldNode, record) {
return evaluateBooleanExpr(fieldNode.invisible, record.evalContextWithVirtualIds);
}
getFormattedValue(fieldName, record) {
const fieldInfo = this.props.model.popoverFieldNodes[fieldName];
const field = this.props.model.fields[fieldName];
let format;
const formattersRegistry = registry.category("formatters");
if (fieldInfo.widget && formattersRegistry.contains(fieldInfo.widget)) {
format = formattersRegistry.get(fieldInfo.widget);
} else {
format = formattersRegistry.get(field.type);
}
return format(record.data[fieldName]);
}
computeDateTimeAndDuration() {
const record = this.props.record;
const { start, end } = record;
const isSameDay = start.hasSame(end, "day");
if (!record.isTimeHidden && !record.isAllDay && isSameDay) {
const timeFormat = is24HourFormat() ? "HH:mm" : "hh:mm a";
this.time = `${start.toFormat(timeFormat)} - ${end.toFormat(timeFormat)}`;
const duration = end.diff(start, ["hours", "minutes"]);
const formatParts = [];
if (duration.hours > 0) {
const hourString = duration.hours === 1 ? _t("hour") : _t("hours");
formatParts.push(`h '${hourString}'`);
}
if (duration.minutes > 0) {
const minuteStr = duration.minutes === 1 ? _t("minute") : _t("minutes");
formatParts.push(`m '${minuteStr}'`);
}
this.timeDuration = duration.toFormat(formatParts.join(", "));
}
if (!this.props.model.isDateHidden) {
this.date = getFormattedDateSpan(start, end);
if (record.isAllDay) {
if (isSameDay) {
this.dateDuration = _t("All day");
} else {
const duration = end.plus({ day: 1 }).diff(start, "days");
this.dateDuration = duration.toFormat(`d '${_t("days")}'`);
}
}
}
}
onEditEvent() {
Registry / API
- Registry name
CalendarCommonPopover- Category
—- Module
web- Slug
calendar-common-popover- Nav group
overlay