# calendar

Month/week/day calendar. Headless: it renders day cells into a container,
places events into their day, supports keyboard date navigation, view
switching, prev/next/today navigation and pointer drag to move events. State is
reflected via `data-*` attributes and CSS custom properties — it applies no
visual styles.

## Markup

You provide the shell; the controller fills `[data-c42-calendar-grid]`.

```html
<div data-c42-calendar>
  <div data-c42-calendar-header>
    <button data-c42-calendar-prev aria-label="Previous">‹</button>
    <button data-c42-calendar-today>Today</button>
    <button data-c42-calendar-next aria-label="Next">›</button>
    <span data-c42-calendar-title></span>
    <button data-c42-calendar-view="month">Month</button>
    <button data-c42-calendar-view="week">Week</button>
    <button data-c42-calendar-view="day">Day</button>
  </div>
  <div data-c42-calendar-grid></div>
</div>
```

### Markup parts

| Selector | Role |
|----------|------|
| `[data-c42-calendar]` | Root (carries `data-view`) |
| `[data-c42-calendar-grid]` | Render target (becomes `role="grid"`, focusable) |
| `[data-c42-calendar-title]` | Filled with the period title (optional) |
| `[data-c42-calendar-prev]` / `[data-c42-calendar-next]` | Step one period |
| `[data-c42-calendar-today]` | Jump to today |
| `[data-c42-calendar-view="month\|week\|day"]` | Switch view (reflects `aria-pressed` + `data-active`) |

### Generated cell structure

```html
<div data-c42-calendar-weekday role="columnheader">Mon</div>
<div data-c42-calendar-day data-date="2026-06-24" role="gridcell"
     data-today data-selected data-focused data-outside tabindex="0|-1">
  <div data-c42-calendar-daynumber>24</div>
  <div data-c42-calendar-events>
    <button data-c42-calendar-event data-event-id="e1"
            style="--c42-calendar-event-start: 0.375; --c42-calendar-event-end: 0.5">
      Standup
    </button>
  </div>
</div>
```

`--c42-calendar-event-start` / `--c42-calendar-event-end` are fractions of the
day (0..1) for time-based positioning in week/day views — kept as fractions so
positioning is layout-agnostic.

## Options

```ts
import { Calendar } from '@42/core/calendar';

new Calendar(root, {
  defaultDate: new Date(),   // anchor date (Date or parseable string)
  view: 'month',             // 'month' | 'week' | 'day'
  weekStartsOn: 0,           // 0 = Sunday … 6 = Saturday
  locale: 'en-US',           // title + weekday formatting
  events: [
    { id: 'e1', title: 'Standup', start: new Date(2026, 5, 16, 9, 0), end: new Date(2026, 5, 16, 9, 30) },
  ],
});
```

> Pass `Date` objects (or full ISO datetimes) for events. A bare `YYYY-MM-DD`
> string is interpreted as a **local** date.

## Interaction

- **Navigate**: prev/next step by month/week/day depending on the view; the
  view buttons switch the view; today returns to the current period.
- **Keyboard** (grid focused): `ArrowLeft`/`ArrowRight` move one day,
  `ArrowUp`/`ArrowDown` move one week (or one day in day view), `Home`/`End` go
  to the start/end of the week, `PageUp`/`PageDown` change period, `Enter`/Space
  select the focused day. Focus uses a roving tabindex on the focused cell.
- **Select**: clicking a day cell (or Enter) selects it → `calendar:select`.
- **Move events**: drag an event onto another day → `calendar:eventmove` (time
  of day and duration are preserved). Clicking an event → `calendar:eventclick`.

## Methods

| Method | Description |
|--------|-------------|
| `setView(view)` | Switch month/week/day |
| `navigate(direction)` | Step period by the sign of `direction` |
| `goToToday()` | Jump to today |
| `goToDate(date)` | Anchor on a specific date (keeps view) |
| `selectDate(date)` | Select + emit `calendar:select` |
| `addEvent(input)` | Add an event (returns the normalized event) |
| `removeEvent(id)` | Remove by id (returns boolean) |
| `moveEvent(id, toDay)` | Move to another day, preserving time; emits `calendar:eventmove` |
| `getEvents()` | All events sorted by start |
| `currentView` / `currentDate` / `selectedDate` | Getters |
| `destroy()` | Remove all listeners |

## Events

| Event | Detail |
|-------|--------|
| `calendar:select` | `{ date, dateKey }` |
| `calendar:viewchange` | `{ view }` |
| `calendar:navigate` | `{ date, view }` |
| `calendar:eventclick` | `{ id, event }` |
| `calendar:eventmove` | `{ id, event, start, end }` |

## Notes

- The grid is fully re-rendered on every state change; read state from events or
  the getters rather than caching cell references.
- Event drag relies on `document.elementFromPoint`; under jsdom it is a no-op,
  so drive moves through `moveEvent()` in tests.
