Calendar/CLAUDE.md
Janus C. H. Knudsen 80aaab46f2 WIP on master
2025-11-03 14:54:57 +01:00

8.2 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Build and Development Commands

TypeScript Build

  • Build: npm run build - Uses esbuild with NovaDI plugin to bundle to wwwroot/js/calendar.js
  • Watch: npm run watch - Auto-rebuild on file changes
  • Clean: npm run clean - Remove compiled output

Testing

  • Run tests: npm test or vitest - Interactive watch mode
  • Run once: npm run test:run or vitest run
  • Test UI: npm run test:ui - Visual test interface
  • Tests use Vitest with jsdom environment (see vitest.config.ts)

CSS Build

  • Build CSS: npm run css:build - PostCSS with nesting support
  • Watch CSS: npm run css:watch - Auto-rebuild CSS on changes
  • Production CSS: npm run css:build:prod - Minified with PurgeCSS
  • Analyze CSS: npm run css:analyze - CSS statistics and analysis

Server

  • Start: dotnet run - ASP.NET Core Kestrel server on http://localhost:8000

Architecture Overview

Core Architectural Pattern

This is a manager-based, event-driven calendar application using pure TypeScript with no UI frameworks. Communication happens exclusively through DOM CustomEvents via a central EventBus.

Key Principles:

  • No global state - State lives in managers
  • Event-driven - All inter-component communication via CustomEvents (see CoreEvents constants)
  • Dependency Injection - Uses @novadi/core DI container
  • Pure DOM - No React/Vue/Angular, just vanilla TypeScript + DOM manipulation

Dependency Injection Flow

The application initializes in src/index.ts following this sequence:

  1. CalendarConfig.initialize() - Static config from DOM attributes (<swp-calendar>)
  2. Container setup - Register all services, managers, renderers, utilities
  3. Manager initialization - CalendarManager coordinates all other managers
  4. Deep linking - Handle URL-based event navigation

All dependencies are auto-wired using NovaDI's @inject decorators (configured in build.js).

Event System

EventBus (src/core/EventBus.ts) wraps DOM CustomEvents with debugging/logging:

// Emit
eventBus.emit('view:changed', { view: 'week', date: new Date() });

// Listen
eventBus.on('view:changed', (event: CustomEvent) => {
  const { view, date } = event.detail;
});

Core events are defined in src/constants/CoreEvents.ts (~20 essential events organized by category: lifecycle, view, navigation, data, grid, event management, system, filter, rendering).

Manager Architecture

Managers are the core organizational units. Each has a specific responsibility:

Primary Managers:

  • CalendarManager - Main coordinator, initializes all managers
  • ViewManager - Handles view switching (day/week/month)
  • NavigationManager - Prev/next/today navigation, date changes
  • EventManager - Event CRUD operations, selection, lifecycle
  • GridManager - Calendar grid structure and layout
  • HeaderManager - Date headers and column rendering
  • AllDayManager - All-day event section management

Interaction Managers:

  • DragDropManager - Event drag-and-drop functionality
  • ResizeHandleManager - Event resize handles
  • DragHoverManager - Visual feedback during drag operations
  • EdgeScrollManager - Auto-scroll when dragging near edges
  • ScrollManager - Grid scroll behavior

Support Managers:

  • ConfigManager - Event-driven config updates (wraps CalendarConfig) and manages CSS custom properties
  • EventLayoutCoordinator - Coordinates event positioning
  • EventStackManager - Handles overlapping events
  • EventFilterManager - Filter events by criteria
  • WorkHoursManager - Work hours highlighting

Renderer Architecture

Renderers handle DOM creation and updates (separation of concerns from managers):

  • EventRenderingService - Main event rendering coordinator
  • DateEventRenderer / AllDayEventRenderer - Event DOM generation
  • DateHeaderRenderer - Date header rendering
  • DateColumnRenderer - Column structure
  • GridRenderer - Grid structure and time slots
  • NavigationRenderer - Navigation controls

Core Services

CalendarConfig (src/core/CalendarConfig.ts):

  • Static configuration class
  • Loads settings from DOM data attributes on <swp-calendar> element
  • Provides computed values (hourHeight, snapInterval, totalSlots, etc.)
  • ConfigManager wraps it for event-driven updates and automatically syncs CSS custom properties to the DOM

DateService (src/utils/DateService.ts):

  • Uses date-fns and date-fns-tz for date calculations
  • Default timezone: Europe/Copenhagen, locale: da-DK

TimeFormatter (src/utils/TimeFormatter.ts):

  • Consistent time/date formatting across the app
  • Configured via CalendarConfig

PositionUtils (src/utils/PositionUtils.ts):

  • Convert between pixels and times
  • Snap-to-grid calculations

URLManager (src/utils/URLManager.ts):

  • Deep linking to events
  • Parses eventId from URL

Repository Pattern

Event data is accessed through IEventRepository interface:

  • MockEventRepository - Current implementation using mock data from wwwroot/data/mock-events.json
  • Ready for API implementation swap

Code Organization

src/
├── constants/      # CoreEvents and other constants
├── core/          # EventBus, CalendarConfig (core infrastructure)
├── data/          # Data models and utilities
├── elements/      # Custom HTML elements (if any)
├── managers/      # Manager classes (business logic)
├── renderers/     # DOM rendering logic
├── repositories/  # Data access layer (IEventRepository, MockEventRepository)
├── types/         # TypeScript interfaces and types
├── utils/         # Utility functions (DateService, PositionUtils, etc.)
└── index.ts       # Application entry point and DI setup

Important Patterns

Adding a New Manager

  1. Create in src/managers/YourManager.ts
  2. Use @inject for dependencies
  3. Implement optional initialize() method if needed
  4. Register in src/index.ts DI container
  5. Listen to events via eventBus.on() (injected as IEventBus)
  6. Emit events via eventBus.emit()

Event Naming Convention

Events follow category:action pattern:

  • view:changed, view:rendered
  • nav:date-changed, nav:navigation-completed
  • data:loaded, data:error
  • event:created, event:updated, event:deleted
  • grid:rendered, grid:clicked

Grid Positioning

Events are positioned using CSS Grid and absolute positioning:

  • Time slots are calculated via CalendarConfig.slotHeight and minuteHeight
  • PositionUtils handles pixel ↔ time conversions
  • Snap-to-grid uses CalendarConfig.getGridSettings().snapInterval

Work Week Configuration

CalendarConfig supports work week presets:

  • standard - Mon-Fri (default)
  • compressed - Mon-Thu
  • midweek - Wed-Fri
  • weekend - Sat-Sun
  • fullweek - Mon-Sun

Change via CalendarConfig.setWorkWeek('preset-id')

Testing

Tests are written using Vitest with jsdom. Setup file: test/setup.ts

Run individual test file:

vitest run path/to/test-file.test.ts

CSS Architecture

CSS is modular and built with PostCSS:

  • Source: wwwroot/css/src/ (uses PostCSS nesting)
  • Output: wwwroot/css/
  • Main file: calendar.css (currently used)

Planned modular CSS files:

  • calendar-base-css.css - Variables and base styles
  • calendar-components-css.css - UI components
  • calendar-events-css.css - Event styling
  • calendar-layout-css.css - Grid layout
  • calendar-popup-css.css - Modals and popups

Debugging

Enable EventBus debug mode (already enabled in src/index.ts):

eventBus.setDebug(true);

Access debug interface in browser console:

window.calendarDebug.eventBus.getEventLog(); // All events
window.calendarDebug.eventManager; // Access EventManager
window.calendarDebug.calendarManager; // Access CalendarManager

Configuration via HTML

Set calendar options via data attributes on <swp-calendar>:

<swp-calendar
  data-view="week"
  data-week-days="7"
  data-snap-interval="15"
  data-day-start-hour="6"
  data-day-end-hour="22"
  data-hour-height="60"
  data-fit-to-width="false">
</swp-calendar>