diff --git a/src/public/app/widgets/view_widgets/calendar_view.ts b/src/public/app/widgets/view_widgets/calendar_view.ts
index d869b4b5d..0421fe4db 100644
--- a/src/public/app/widgets/view_widgets/calendar_view.ts
+++ b/src/public/app/widgets/view_widgets/calendar_view.ts
@@ -48,6 +48,11 @@ const TPL = `
.calendar-container .fc-button {
padding: 0.2em 0.5em;
}
+
+ .calendar-container .promoted-attribute {
+ font-size: 0.85em;
+ opacity: 0.85;
+ }
@@ -119,13 +124,24 @@ export default class CalendarView extends ViewMode {
height: "100%",
eventContent: (e => {
let html = "";
+ const { iconClass, promotedAttributes } = e.event.extendedProps;
- const iconClass = e.event.extendedProps.iconClass;
+ // Title and icon
if (iconClass) {
html += `
`;
}
-
html += utils.escapeHtml(e.event.title);
+
+ // Promoted attributes
+ if (promotedAttributes) {
+ for (const [ name, value ] of Object.entries(promotedAttributes)) {
+ html += `\
+
+ ${name}: ${value}
+
`;
+ }
+ }
+
return { html };
}),
dateClick: async (e) => {
@@ -306,27 +322,22 @@ export default class CalendarView extends ViewMode {
const events: EventInput[] = [];
// the user can specify one or multiple attributes to be promoted onto the calendar view by setting `#calendar:promotedAttributes` at the note level
// their values will then be rentered into the event title and appear as "[eventIcon] $eventTitle [#promotedAttributeX=valueX] [#promotedAttributeY=valueY]"
- const promotedAttrs = note
- .getAttributes()
- .filter((attr) => attr.type == "label" && attr.name == "calendar:promotedAttribute")
- .map((attr) => attr.value.substring(1));
- let titleExtended = "";
- if (promotedAttrs && promotedAttrs.length) {
- const promotedValues = note
- .getAttributes()
- .filter((attr) => promotedAttrs.includes(attr.name))
- .map((attr) => [attr.name, attr.value]);
- for (const defined of promotedValues) titleExtended = titleExtended + ` [#${defined[0]}="${defined[1]}"]`;
+
+ const calendarPromotedAttributes = note.getLabelValue("calendar:promotedAttributes");
+ let promotedAttributesData = null;
+ if (calendarPromotedAttributes) {
+ promotedAttributesData = await this.#buildPromotedAttributes(note, calendarPromotedAttributes);
}
for (const title of titles) {
const eventData: EventInput = {
- title: title + titleExtended,
+ title: title,
start: startDate,
url: `#${note.noteId}`,
noteId: note.noteId,
color: color ?? undefined,
- iconClass: note.getLabelValue("iconClass")
+ iconClass: note.getLabelValue("iconClass"),
+ promotedAttributes: promotedAttributesData
};
const endDateOffset = CalendarView.#offsetDate(endDate ?? startDate, 1);
@@ -338,6 +349,35 @@ export default class CalendarView extends ViewMode {
return events;
}
+ static async #buildPromotedAttributes(note: FNote, calendarPromotedAttributes: string) {
+ const promotedAttributeNames = calendarPromotedAttributes.split(",");
+ const filteredPromotedAttributes = note.getPromotedDefinitionAttributes().filter((attr) => promotedAttributeNames.includes(attr.name));
+ const result: Record
= {};
+
+ for (const promotedAttribute of filteredPromotedAttributes) {
+ const [ type, name ] = promotedAttribute.name.split(":", 2);
+ const definition = promotedAttribute.getDefinition();
+
+ if (definition.multiplicity !== "single") {
+ // TODO: Add support for multiple definitions.
+ continue;
+ }
+
+ // TODO: Add support for relations
+ if (type !== "label" || !note.hasLabel(name)) {
+ continue;
+ }
+
+ const value = note.getLabelValue(name);
+ const friendlyName = definition.promotedAlias ?? name;
+ if (friendlyName && value) {
+ result[friendlyName] = value;
+ }
+ }
+
+ return result;
+ }
+
static async #parseCustomTitle(customTitleValue: string | null, note: FNote, allowRelations = true): Promise {
if (customTitleValue) {
const attributeName = customTitleValue.substring(1);