238 lines
8.2 KiB
Markdown
238 lines
8.2 KiB
Markdown
|
|
# 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:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 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:
|
||
|
|
```bash
|
||
|
|
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`):
|
||
|
|
```typescript
|
||
|
|
eventBus.setDebug(true);
|
||
|
|
```
|
||
|
|
|
||
|
|
Access debug interface in browser console:
|
||
|
|
```javascript
|
||
|
|
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>`:
|
||
|
|
```html
|
||
|
|
<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>
|
||
|
|
```
|