# `hb-calendar-appointments`

**Category:** calendar · **Tags:** calendar, appointments

## Summary

A **month agenda** web component: it shows **only events in the visible month**, grouped **by calendar day**, with **per-day headings** and **clickable rows** for each appointment. An optional **header** switches the month and exposes slots for labels and navigation chrome.

## What it does

- Filters `events` to the month implied by `date` (month + year).
- Sorts events by `date` when `events` is parsed from a string.
- Renders one block per day that has at least one event: weekday (long, locale from `navigator.languages[0]` or `en`), day-of-month number, then rows ordered as in the sorted list.
- Each row shows a Bootstrap Icons dot (`bi-dot`), time (`HH:mm`), and `label`; optional per-event `color` overrides the icon color via inline style.
- Month navigation (`disable_header` off) updates `date` and emits **`changeCalendarDate`**.
- Row click emits **`calendarEventClick`** with the event `id`.

The `date-holidays` package is imported and an `IT` instance is constructed in script, but **that object is not used** anywhere in the template or derived data (no holiday-based labels or filtering in the current implementation).

## UI / layout

| Region | Behavior |
|--------|------------|
| **Header** (optional) | Flex row: default title (month name + year via `Intl` + `dayjs`), prev/next controls. Wrapped in `part="calendar-header"`. Inner title span uses `part="calendar-current-time-header"`. Entire header hidden when `disable_header` is enabled (see logic below). |
| **Slots in header** | `header` wraps title + nav; `calendar_month` replaces default month/year text; `header_month_icon_prev` / `header_month_icon_next` replace default buttons (still wired to `changeMonth(-1)` / `changeMonth(1)` via outer `onclick`). |
| **Agenda list** | Under `#appointments_container`: for each day bucket, a bold day line (`events_day`) then `event_row` blocks (focusable `role="button"`). If the month has no events, the list area is empty (no placeholder). |

Icons are loaded for the shadow tree via `styles/webcomponent.scss` (Bootstrap Icons font). A `<svelte:head>` stylesheet link is also present but does not apply inside shadow DOM by itself.

## Logic

- **Visible month:** `month` / `year` derived from `date` (`dayjs`).
- **Filtering:** `monthsEvent` keeps events whose month and year match `date`.
- **Grouping:** `eventsOfThisMonthByDay` buckets by day-of-month (`D`); first occurrence of a day creates the bucket, further events append to that bucket’s array.
- **`disable_header`:** In `$effect`, string values are normalized: `true` / `yes` / `""` (empty) → header hidden; otherwise shown.
- **`events`:** If a string, `JSON.parse`; then sorted ascending by `new Date(a.date)` vs `new Date(b.date)`.
- **`selected`:** If a string, parsed with `dayjs(selected).toDate()` for in-component use. There is a **`selectDay`** helper that sets `selected` and would emit **`changeSelectedDate`**, but **nothing in the default markup calls `selectDay`**, so that event is **not** produced by the shipped UI (only the typed API / future wiring).

## Custom element

```text
hb-calendar-appointments
```

## Attributes / properties

HTML attributes are strings. Align with your bridge; the component’s `$effect` coerces some values.

| Name | Typing (`Component`) | Notes |
|------|----------------------|--------|
| `id` | `string` (optional) | Passed through like other props; host `id` if set as attribute. |
| `date` | `Date` (optional) | Default: start of current month (`dayjs().startOf("month")`). Drives which month’s events are shown. From HTML, use an ISO-like string your runtime parses to `Date` / or set the property in JS. |
| `events` | `IEvent[]` (optional) | JSON array string from attributes; each item: `date`, `label`, `id` required for useful rows; `link`, `icon`, `color` optional (`link` / `icon` are **not read** by the template). |
| `selected` | `Date` (optional) | Parsed from string in `$effect`. No built-in control updates selection or emits `changeSelectedDate`. |
| `disable_header` | `boolean` (optional) | Default `false`. For attributes, this codebase often uses **`yes`** / **`no`**; the effect also treats string `"true"`, `"yes"`, and `""` as hide header. |

### `IEvent` (from typings)

| Field | Type | Used in UI |
|-------|------|------------|
| `date` | `Date` | Filter, sort, time column, weekday/day grouping |
| `label` | `string` | Row text (`aria-label`, visible label) |
| `id` | `string` | `calendarEventClick` payload |
| `link` | optional `string` | No |
| `icon` | optional `string` | No |
| `color` | optional `string` | Icon color (inline `style`) |

## Events (`CustomEvent`)

| Name | `detail` | When emitted (current implementation) |
|------|-----------|----------------------------------------|
| `calendarEventClick` | `{ eventId: string }` | User activates an `.event_row` (click path in template). |
| `changeCalendarDate` | `{ date: Date }` | Prev/next month navigation runs `changeMonth`. |
| `changeSelectedDate` | `{ selectedDate: Date }` | Only if something called `selectDay` — **not wired** in the default Svelte markup. |

## Styling

### CSS custom properties

Documented in `extra/docs.ts`; defaults from code (`styles/webcomponent.scss` + descriptions in docs):

| Variable | Kind | Default / source | Role |
|----------|------|-------------------|------|
| `--hb-calendar-event-button-color` | color | `var(--bulma-link, #485fc7)` on `:host` | Dot icon color when `event.color` is not set. |
| `--bulma-radius` | size | theme / `0.25rem` fallback in SCSS | `border-radius` on `.event_row`. |
| `--bulma-border` | color | theme / `hsl(0deg 0% 86%)` fallback | `outline` on `.event_row:hover`. |

### CSS parts (`::part`)

| Part | Description |
|------|-------------|
| `calendar-header` | Outer header strip (month navigation + title area). |
| `calendar-current-time-header` | Inner title span (month slot + default month/year text). |

## Slots

| Slot | Description |
|------|-------------|
| `header_month_icon_prev` | Replace default “previous month” control (e.g. icon button). |
| `header_month_icon_next` | Replace default “next month” control. |
| `header` | Wraps the whole header row (title + nav); default fills month + controls. |
| `calendar_month` | Replace/wrap visible month/year label in the header row. |

## Typings

Authoring types live in `types/webcomponent.type.d.ts`:

- **`Component`** — `id`, `date`, `events`, `selected`, `disable_header`
- **`IEvent`** — event record shape
- **`Events`** — `calendarEventClick`, `changeCalendarDate`, `changeSelectedDate`

Built outputs (`types/html-elements.d.ts`, `types/svelte-elements.d.ts`) are regenerated with `npm run build:wc`.

## Example HTML

```html
<hb-calendar-appointments
  id="agenda-1"
  disable_header="no"
  date="2026-04-01T00:00:00.000Z"
  events='[
    {"id":"a1","label":"Stand-up","date":"2026-04-17T09:00:00.000Z"},
    {"id":"a2","label":"Review","date":"2026-04-17T15:30:00.000Z","color":"#b86bff"}
  ]'
></hb-calendar-appointments>
```

With hidden header:

```html
<hb-calendar-appointments
  disable_header="yes"
  events='[{"id":"1","label":"Meeting","date":"2026-03-15T10:00:00.000Z"}]'
></hb-calendar-appointments>
```

Listen in JavaScript, for example:

```js
document.querySelector("hb-calendar-appointments")?.addEventListener("calendarEventClick", (e) => {
  console.log(e.detail.eventId);
});
document.querySelector("hb-calendar-appointments")?.addEventListener("changeCalendarDate", (e) => {
  console.log(e.detail.date);
});
```
