1.12.0 visual improvements on the calendar

This commit is contained in:
Michalis Efstratiadis 2026-01-28 17:45:49 +02:00
parent cb6d42e8cc
commit fe246bca0b
No known key found for this signature in database
4 changed files with 150 additions and 9 deletions

View file

@ -1,7 +1,7 @@
{
"id": "memochron",
"name": "MemoChron",
"version": "1.11.1",
"version": "1.12.0",
"minAppVersion": "1.8.9",
"description": "Calendar integration and note creation with support for public iCalendar URLs",
"author": "Michalis Efstratiadis",

View file

@ -52,13 +52,28 @@ export class CalendarView extends ItemView {
this.createUI();
this.registerViewEvents();
this.loadDailyNotes();
await this.refreshEvents();
// If we have a saved height, we should try to determine the best view mode for it
if (this.plugin.settings.calendarHeight && !this.plugin.settings.hideCalendar) {
// Use efficient timeout to allow DOM to settle
setTimeout(() => {
const today = new Date();
this.selectedDate = today;
this.currentDate = today;
// Render first to ensure we can measure row heights
this.renderCalendar();
this.recalculateViewModeFromHeight(this.plugin.settings.calendarHeight);
this.refreshEvents();
}, 50);
} else {
await this.refreshEvents();
}
// If calendar is hidden, show today's agenda
if (this.plugin.settings.hideCalendar) {
this.selectedDate = new Date();
await this.showDayAgenda(this.selectedDate);
} else {
} else if (!this.plugin.settings.calendarHeight) {
await this.goToToday();
}
}
@ -164,7 +179,7 @@ export class CalendarView extends ItemView {
// Apply saved height
if (this.plugin.settings.calendarHeight) {
this.calendar.style.height = `${this.plugin.settings.calendarHeight} px`;
this.calendar.style.height = `${this.plugin.settings.calendarHeight}px`;
}
// Create resize handle
@ -223,6 +238,7 @@ export class CalendarView extends ItemView {
if (this.viewMode !== value) {
this.viewMode = value;
await this.refreshEvents();
await this.snapToCurrentViewMode();
}
});
});
@ -546,7 +562,7 @@ export class CalendarView extends ItemView {
});
// Add daily note dot first if it exists (show on calendar even if not shown in agenda)
if (hasDailyNote) {
if (hasDailyNote && this.plugin.settings.showDailyNoteInAgenda) {
const dailyNoteDot = dotsContainer.createEl("div", {
cls: "memochron-event-dot daily-note-dot colored",
});
@ -575,7 +591,7 @@ export class CalendarView extends ItemView {
});
// Add daily note dot if exists (show on calendar even if not shown in agenda)
if (hasDailyNote) {
if (hasDailyNote && this.plugin.settings.showDailyNoteInAgenda) {
dotsContainer.createEl("div", {
cls: "memochron-event-dot daily-note-dot",
});
@ -943,6 +959,93 @@ export class CalendarView extends ItemView {
await this.showEventDetails(event);
}
// --- Dynamic Resizing Methods ---
private measureRowHeight(): number {
const day = this.calendar.querySelector('.memochron-day');
return day ? (day as HTMLElement).offsetHeight : 0;
}
private measureHeaderHeight(): number {
// Header includes weekday headers + any padding/margins of the grid if relevant
const header = this.calendar.querySelector('.memochron-weekday');
return header ? (header as HTMLElement).offsetHeight : 0;
}
private measureTitleHeight(): number {
// If title is part of the measured height we care about
// But drag handle resizes this.calendar, which contains title inside?
// Wait, createUI says: calendar = container.createEl("div", { cls: "memochron-calendar" });
// And renderCalendar empties this.calendar and adds title + grid.
// So title is inside.
const title = this.calendar.querySelector('.memochron-title'); // Assuming title is inside?
// Actually renderCalendar calls updateTitle which finds .memochron-title in containerEl, NOT in this.calendar
// Let's check createControls... nav has title. createControls is sibling to calendar.
// So this.calendar ONLY contains the grid.
// BUT renderCalendar implementation:
// this.calendar.empty(); ... this.updateTitle(); ... const grid = this.createCalendarGrid();
// updateTitle() looks for .memochron-title in containerEl.
// So this.calendar effectively only contains the grid (and maybe padding).
// Let's check CSS. .memochron-calendar has padding.
return 0;
}
private calculateWeeksThatFit(totalHeight: number): number {
// We need to account for calendar padding
const style = getComputedStyle(this.calendar);
const paddingTop = parseFloat(style.paddingTop) || 0;
const paddingBottom = parseFloat(style.paddingBottom) || 0;
const availableHeight = totalHeight - paddingTop - paddingBottom;
const headerHeight = this.measureHeaderHeight();
const rowHeight = this.measureRowHeight();
if (rowHeight === 0) return 4; // Fallback
const availableForRows = availableHeight - headerHeight;
// We can fit N rows
const weeks = Math.floor(availableForRows / rowHeight);
return Math.max(1, weeks);
}
private recalculateViewModeFromHeight(height: number) {
if (height < 100) return; // Ignore too small
// If we can't measure yet, assume default
if (this.measureRowHeight() === 0) return;
const weeksThatFit = this.calculateWeeksThatFit(height);
// Determine how many weeks are needed for the full month view
// A month can span 4, 5, or 6 weeks.
// Let's approximate: if it fits 6 weeks, it fits a month.
// Even 5 weeks often fits a month.
// Logic: If user drags such that we can show N weeks...
// If N >= weeks in current month, switch to month view.
// Else switch to N weeks view.
const { year, month } = this.getYearMonth();
const { firstDayOffset, daysInMonth } = this.getMonthInfo(year, month);
// Calculate exact weeks needed for THIS month
const days = firstDayOffset + daysInMonth;
const weeksNeededForMonth = Math.ceil(days / 7);
if (weeksThatFit >= weeksNeededForMonth) {
if (this.viewMode !== 'month') {
this.viewMode = 'month';
// We don't call render here, caller does
}
} else {
const newMode = Math.min(5, Math.max(1, weeksThatFit)) as CalendarViewMode;
if (this.viewMode !== newMode) {
this.viewMode = newMode;
}
}
}
private handleDragStart(e: MouseEvent) {
e.preventDefault();
this.dragStartY = e.clientY;
@ -955,8 +1058,20 @@ export class CalendarView extends ItemView {
private handleDragMove(e: MouseEvent) {
const deltaY = e.clientY - this.dragStartY;
const newHeight = Math.max(100, this.dragStartHeight + deltaY); // Min height 100px
const newHeight = Math.max(100, this.dragStartHeight + deltaY);
this.calendar.style.height = `${newHeight}px`;
// Dynamic View Switching
// We only trigger if height changed significantly enough or periodically
// Simple approach: just recalculate.
// To avoid flickering, maybe debounce or only change if different
const oldMode = this.viewMode;
this.recalculateViewModeFromHeight(newHeight);
if (this.viewMode !== oldMode) {
this.renderCalendar();
}
}
private async handleDragEnd(e: MouseEvent) {
@ -964,8 +1079,32 @@ export class CalendarView extends ItemView {
window.removeEventListener("mousemove", this.handleDragMoveBound);
window.removeEventListener("mouseup", this.handleDragEndBound);
await this.snapToCurrentViewMode();
}
private async snapToCurrentViewMode() {
// Snap to nearest valid row height
const rowHeight = this.measureRowHeight();
const headerHeight = this.measureHeaderHeight();
const style = getComputedStyle(this.calendar);
const padding = (parseFloat(style.paddingTop) || 0) + (parseFloat(style.paddingBottom) || 0);
let weeks: number;
if (this.viewMode === 'month') {
const { year, month } = this.getYearMonth();
const { firstDayOffset, daysInMonth } = this.getMonthInfo(year, month);
weeks = Math.ceil((firstDayOffset + daysInMonth) / 7);
} else {
weeks = this.viewMode as number;
}
const idealHeight = padding + headerHeight + (weeks * rowHeight);
// Animate snap if desired, or just set
this.calendar.style.height = `${idealHeight}px`;
// Save height setting
this.plugin.settings.calendarHeight = this.calendar.offsetHeight;
this.plugin.settings.calendarHeight = idealHeight;
await this.plugin.saveSettings();
}

View file

@ -193,6 +193,7 @@
min-height: 8px;
}
.memochron-event-dot.colored {
background-color: currentColor;
margin-bottom: 0;

View file

@ -37,5 +37,6 @@
"1.10.0": "1.8.9",
"1.10.1": "1.8.9",
"1.11.0": "1.8.9",
"1.11.1": "1.8.9"
"1.11.1": "1.8.9",
"1.12.0": "1.8.9"
}