UNPKG

@htmlbricks/hb-calendar-appointments

Version:

Month agenda view: events for the current month are grouped by calendar day and listed chronologically with weekday, day number, time, and colored markers. Optional header with month navigation; changing month or selecting a day dispatches `changeCalendar

147 lines (108 loc) 7.66 kB
# `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); }); ```