summaryrefslogtreecommitdiffhomepage
path: root/.rules/plan/calendar-phase-3.md
blob: 228f7e001dfad94b15be1234abee3e1db112a622 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# Phase 3: Calendar Renderer (`src/calendar/calendar-renderer.ts`)

**Status:** Not started
**Depends on:** Phase 2 (calendar-state.ts — reads state for rendering)
**Output file:** `src/calendar/calendar-renderer.ts`

---

## Overview

Pure DOM rendering — replaces `Calendar.svelte` and `obsidian-calendar-ui`. Builds the month grid using Obsidian's `createEl`/`createDiv` helpers. Subscribes to `CalendarState` and re-renders on changes.

---

## Design

```
class CalendarRenderer:
  constructor(containerEl: HTMLElement, state: CalendarState, callbacks: CalendarCallbacks)

  interface CalendarCallbacks:
    onClickDay(date: Moment, event: MouseEvent): void
    onClickWeek(date: Moment, event: MouseEvent): void
    onClickMonth(date: Moment, event: MouseEvent): void       // from fork: click month label
    onClickYear(date: Moment, event: MouseEvent): void        // from fork: click year label
    onClickQuarter(date: Moment, event: MouseEvent): void     // from fork: click quarter label
    onContextMenuDay(date: Moment, event: MouseEvent): void
    onContextMenuWeek(date: Moment, event: MouseEvent): void
    onContextMenuMonth(date: Moment, event: MouseEvent): void  // from fork
    onContextMenuYear(date: Moment, event: MouseEvent): void   // from fork
    onContextMenuQuarter(date: Moment, event: MouseEvent): void // from fork
    onHoverDay(date: Moment, targetEl: EventTarget, isMetaPressed: boolean): void
    onHoverWeek(date: Moment, targetEl: EventTarget, isMetaPressed: boolean): void
    onHoverMonth(date: Moment, targetEl: EventTarget, isMetaPressed: boolean): void    // from fork
    onHoverYear(date: Moment, targetEl: EventTarget, isMetaPressed: boolean): void     // from fork
    onHoverQuarter(date: Moment, targetEl: EventTarget, isMetaPressed: boolean): void  // from fork

  Methods:
  - render(): void
      Clears containerEl, builds:
        - Navigation bar: [<] [Month Year] [>] [Today]
          - Month and Year labels are clickable → callbacks.onClickMonth / onClickYear (from fork)
          - Quarter label (e.g. Q1) shown if calendarShowQuarter is true (from fork)
        - Weekday headers row (Mon, Tue, ...)
        - Optional week number column (left or right based on calendarShowWeekNumbersRight — from fork)
        - 6 rows × 7 day cells
      Each day cell:
        - CSS class: "today", "has-note", "active", "other-month"
        - Dots container (word count, tasks — uses settings from Phase 5)
        - Click handler → callbacks.onClickDay
        - Context menu → callbacks.onContextMenuDay

  - destroy(): void
      Cleanup intervals, event listeners

  private:
  - renderNavBar(): HTMLElement
  - renderDayHeaders(): HTMLElement
  - renderWeeks(): HTMLElement
  - renderDay(date: Moment): HTMLElement
  - renderWeekNumber(date: Moment, position: "left" | "right"): HTMLElement  // from fork: position option
  - renderQuarterLabel(date: Moment): HTMLElement                            // from fork
  - getDaysInMonthGrid(month: Moment): Moment[][]
      Returns 6 rows of 7 days, padding with prev/next month days
```

---

## Notes

- All rendering uses Obsidian's `createEl`/`createDiv` helpers — no innerHTML.
- CSS classes follow the `ai-pulse-calendar-*` naming convention (see Phase 9).
- The renderer needs access to settings (week start, show week numbers, show quarter, words per dot) — these are passed via constructor or a settings reference.
- Word count dots: read the file's cached metadata or content to count words, show N dots where N = floor(wordCount / wordsPerDot), capped at a reasonable max.