feat: added optional text description for event

This commit is contained in:
Aleksey Korolev 2026-01-22 13:34:44 +03:00
parent e6f00b51ae
commit bb9ff01bba
2 changed files with 103 additions and 19 deletions

78
main.ts
View file

@ -1,4 +1,4 @@
import { Plugin, moment, setIcon, setTooltip } from 'obsidian'; import { MarkdownRenderer, Plugin, moment, setIcon, setTooltip } from 'obsidian';
import { SettingsTab } from 'settings-tab'; import { SettingsTab } from 'settings-tab';
interface PluginSettings { interface PluginSettings {
@ -40,27 +40,33 @@ export default class EventHighlightPlugin extends Plugin {
this.addSettingTab(new SettingsTab(this.app, this)); this.addSettingTab(new SettingsTab(this.app, this));
this.registerMarkdownCodeBlockProcessor('event-highlight', (source, el, ctx) => { this.registerMarkdownCodeBlockProcessor(
this.renderElement(el, source.trim()); 'event-highlight',
async (source, el, ctx) => {
await this.renderElement(el, source.trim(), undefined, ctx.sourcePath);
el.addEventListener('click', () => { el.addEventListener('click', async () => {
const { swapped } = this.loadState(el); const { swapped } = this.loadState(el);
this.renderElement(el, source.trim(), { await this.renderElement(
swapped: !swapped, el,
source.trim(),
{ swapped: !swapped },
ctx.sourcePath,
);
}); });
}); },
}); );
this.registerEvent( this.registerEvent(
this.app.workspace.on('active-leaf-change', () => { this.app.workspace.on('active-leaf-change', () => {
this.updateAllPage(); void this.updateAllPage();
}), }),
); );
this.registerInterval( this.registerInterval(
window.setInterval(() => { window.setInterval(() => {
this.updateAllPage(); void this.updateAllPage();
}, 10_000), }, 10_000),
); );
} }
@ -115,10 +121,17 @@ export default class EventHighlightPlugin extends Plugin {
return JSON.stringify(oldState) === JSON.stringify(newState); return JSON.stringify(oldState) === JSON.stringify(newState);
} }
private renderElement(el: Element, source: string, overrideState?: Partial<State>) { private async renderElement(
const parsedDate = moment(source, ['YYYY-MM-DD', 'DD.MM.YYYY'], true); el: Element,
source: string,
overrideState?: Partial<State>,
sourcePath = '',
) {
const { dateSource, extraText } = this.parseSource(source);
const parsedDate = moment(dateSource, ['YYYY-MM-DD', 'DD.MM.YYYY'], true);
const parsedDateTime = moment( const parsedDateTime = moment(
source, dateSource,
['YYYY-MM-DD HH:mm', 'DD.MM.YYYY HH:mm'], ['YYYY-MM-DD HH:mm', 'DD.MM.YYYY HH:mm'],
true, true,
); );
@ -214,14 +227,27 @@ export default class EventHighlightPlugin extends Plugin {
el.empty(); el.empty();
const dateSpan = el.createEl('div', { cls: 'event-highlight-badge' }); const container = el.createEl('div', { cls: 'event-highlight-container' });
if (extraText) {
const textEl = container.createEl('div', { cls: 'event-highlight-text' });
await MarkdownRenderer.render(this.app, extraText, textEl, sourcePath, this);
}
const dateSpan = container.createEl('div', { cls: 'event-highlight-badge' });
const iconSpan = dateSpan.createEl('div', { cls: 'icon' }); const iconSpan = dateSpan.createEl('div', { cls: 'icon' });
if (extraText) {
dateSpan.classList.add('with-text');
}
dateSpan.createEl('div', { text }); dateSpan.createEl('div', { text });
switch (true) { switch (true) {
case isError: case isError:
dateSpan.classList.add('error'); dateSpan.classList.add('error');
container.classList.add('error');
setIcon(iconSpan, 'ban'); setIcon(iconSpan, 'ban');
setTooltip(dateSpan, 'Invalid date'); setTooltip(dateSpan, 'Invalid date');
@ -229,6 +255,7 @@ export default class EventHighlightPlugin extends Plugin {
break; break;
case isAfter: case isAfter:
dateSpan.classList.add('after'); dateSpan.classList.add('after');
container.classList.add('after');
setIcon(iconSpan, 'calendar-check-2'); setIcon(iconSpan, 'calendar-check-2');
setTooltip(dateSpan, 'Past event'); setTooltip(dateSpan, 'Past event');
@ -236,6 +263,7 @@ export default class EventHighlightPlugin extends Plugin {
break; break;
case isBefore: case isBefore:
dateSpan.classList.add('before'); dateSpan.classList.add('before');
container.classList.add('before');
setIcon(iconSpan, 'calendar'); setIcon(iconSpan, 'calendar');
setTooltip(dateSpan, `Event soon (${workFormatted})`); setTooltip(dateSpan, `Event soon (${workFormatted})`);
@ -243,6 +271,7 @@ export default class EventHighlightPlugin extends Plugin {
break; break;
case isUpcoming: case isUpcoming:
dateSpan.classList.add('upcoming'); dateSpan.classList.add('upcoming');
container.classList.add('upcoming');
setIcon(iconSpan, 'calendar-clock'); setIcon(iconSpan, 'calendar-clock');
setTooltip(dateSpan, `Upcoming event (${workFormatted})`); setTooltip(dateSpan, `Upcoming event (${workFormatted})`);
@ -250,6 +279,7 @@ export default class EventHighlightPlugin extends Plugin {
break; break;
case isActual: case isActual:
dateSpan.classList.add('actual'); dateSpan.classList.add('actual');
container.classList.add('actual');
setIcon(iconSpan, 'clock'); setIcon(iconSpan, 'clock');
setTooltip(dateSpan, `Event started (${workFormatted})`); setTooltip(dateSpan, `Event started (${workFormatted})`);
@ -258,15 +288,25 @@ export default class EventHighlightPlugin extends Plugin {
} }
} }
private updateAllPage() { private async updateAllPage() {
const elements = document.querySelectorAll(`[${this.attributeStateName}]`); const elements = document.querySelectorAll(`[${this.attributeStateName}]`);
elements.forEach((el) => { const sourcePath = this.app.workspace.getActiveFile()?.path ?? '';
for (const el of Array.from(elements)) {
const { source } = this.loadState(el); const { source } = this.loadState(el);
if (!source) return; if (!source) return;
this.renderElement(el, source); await this.renderElement(el, source, undefined, sourcePath);
}); }
}
private parseSource(source: string) {
const lines = source.split(/\r?\n/);
const dateSource = (lines[0] || '').trim();
const extraText = lines.slice(1).join('\n').trim();
return { dateSource, extraText };
} }
} }

View file

@ -1,3 +1,21 @@
.event-highlight-container {
display: inline-flex;
flex-direction: column;
align-items: flex-start;
gap: 6px;
border: 1px solid transparent;
border-radius: 4px;
}
.event-highlight-text {
margin: 6px;
margin-bottom: 0;
color: var(--text-normal);
}
.event-highlight-badge { .event-highlight-badge {
padding: 2px 4px; padding: 2px 4px;
@ -12,21 +30,39 @@
cursor: default; cursor: default;
} }
.event-highlight-badge.with-text {
width: 100%;
border-radius: 0 0 4px 4px;
}
.event-highlight-badge.after { .event-highlight-badge.after {
background-color: var(--background-secondary-alt); background-color: var(--background-secondary-alt);
color: var(--mono-rgb-255); color: var(--mono-rgb-255);
} }
.event-highlight-container.after {
border-color: var(--background-secondary-alt);
}
.event-highlight-badge.before { .event-highlight-badge.before {
background-color: rgba(var(--color-green-rgb), 0.2); background-color: rgba(var(--color-green-rgb), 0.2);
color: var(--color-green); color: var(--color-green);
} }
.event-highlight-container.before {
border-color: rgba(var(--color-green-rgb), 0.2);
}
.event-highlight-badge.upcoming { .event-highlight-badge.upcoming {
background-color: rgba(var(--color-yellow-rgb), 0.2); background-color: rgba(var(--color-yellow-rgb), 0.2);
color: var(--color-yellow); color: var(--color-yellow);
} }
.event-highlight-container.upcoming {
border-color: rgba(var(--color-yellow-rgb), 0.2);
}
.event-highlight-badge.actual { .event-highlight-badge.actual {
background-color: rgba(var(--color-purple-rgb), 0.2); background-color: rgba(var(--color-purple-rgb), 0.2);
color: var(--color-purple); color: var(--color-purple);
@ -34,6 +70,10 @@
font-weight: bold; font-weight: bold;
} }
.event-highlight-container.actual {
border-color: rgba(var(--color-purple-rgb), 0.2);
}
.event-highlight-badge.error { .event-highlight-badge.error {
background-color: rgba(var(--color-red-rgb), 0.2); background-color: rgba(var(--color-red-rgb), 0.2);
color: var(--color-red); color: var(--color-red);
@ -41,6 +81,10 @@
font-weight: bold; font-weight: bold;
} }
.event-highlight-container.error {
border-color: rgba(var(--color-red-rgb), 0.2);
}
.event-highlight-badge .icon { .event-highlight-badge .icon {
display: inline-flex; display: inline-flex;