@htmlbricks/hb-calendar-events
v0.76.5
Published
Classic month grid (7-day week header and variable rows) with selectable cells, “today” styling, and event chips inside each day. Supports adjacent-month padding cells, Italian holidays, JSON `events`, and the same navigation/selection events as the appoi
Downloads
8,178
Readme
hb-calendar-events (calendar-events)
Category: calendar · Tags: calendar · Package: @htmlbricks/hb-calendar-events
Summary
hb-calendar-events is a Bulma-styled month calendar rendered as a full-width table: a localized weekday header (short names, order Monday → Sunday), a variable number of week rows, and per-day cells that can show muted “padding” days from the previous/next month. Users can change the visible month, select a day (current month or padding cells), and click small event chips mapped from a JSON events list. The component dispatches changeCalendarDate, changeSelectedDate, and calendarEventClick on the host for integration with host apps or sibling calendars.
Behavior
- Visible month is anchored by the
dateprop: internally,month/yearare derived from that value. The default is the first day of the current month (dayjs().startOf("month")). - Header (month title + prev/next controls) is shown unless
disable_headeris enabled. Prev/next callchangeMonth(±1), updatedate, and dispatchchangeCalendarDatewith{ date }(aDatefor the new month anchor). - Weekday labels use
Intl.DateTimeFormatwithweekday: "short"and the browser’s primary language (navigator.languages[0], falling back to"en"). Column order is fixed in markup as Mon–Sat fromstartOf("week") + 1..6days and Sunday in the last column (startOf("week")). - Grid size:
rows = ceil((daysInMonth + dayOfWeekOfMonthStart) / 7)wheredayOfWeekfollows dayjs’sday()(0 = Sunday … 6 = Saturday). The first row mixes previous-month padding cells and current-month days; the last row mixes current-month days and next-month padding cells; middle rows are only current month. - Day selection: clicking a
<td>(cell) setsselectedto the logical calendar date for that cell and dispatcheschangeSelectedDatewith{ selectedDate }. Padding cells select dates in the adjacent month (previous or next), not only the visible month. - Events: optional
eventsarray is filtered per month for the visible month, previous month, and next month so padding cells can still show chips for events that fall on those dates. For each day, chips match events whose calendar day (DD) equals the cell’s day number in the month that cell represents (current, previous, or next). Each chip is a<button>; its click handler dispatchescalendarEventClickwith{ eventId }(the event’sidstring). Chiponclickdoes not stop propagation; hosts that also listen on the cell should account for possible bubbling if they attach listeners outside the shadow root. IEvent.linkandIEvent.iconexist in typings but are not used in the current template (no link or icon rendering).date-holidays: the module is imported andnew Holidays("IT")is constructed, but no holiday data is read or displayed in the current markup—do not rely on Italian public holidays in the UI until that logic is wired.
disable_header coercion
Inside $effect, if disable_header is a string, it is treated as true when (case-insensitive) "true" or "yes", or when the string is empty ""; otherwise false. From HTML attributes, expect the usual string forms your custom-element layer maps to this prop.
events and selected coercion
- If
eventsis a string, it isJSON.parsed into an array (invalid JSON will throw at runtime). - If
selectedis a string, it is parsed withdayjs(selected).toDate()(pass a value dayjs understands, e.g. ISO 8601).
Graphics and layout
- Structure: Optional header
div(part="calendar-header") plus a single<table class="table is-fullwidth hb-calendar-table">with<thead>(weekdays) and<tbody>(day rows). - Table:
width: 100%,height: 100%,table-layout: fixed,min-height: 34.375rem,border-collapse: collapse. Each day cell hasvertical-align: baseline, a 1px border using--hb-calendar-cell-border, and row height split evenly via inlinestyle="height: {100/rows}%;"on<td>. - Header (when enabled): flex row with spacing (
is-flex,is-justify-content-space-between, etc.), default month + year text in a title-sized span, and default small light buttons˂/˃for prev/next unless slots override. - Cells: day number in a
.cell-datediv; padding days use.cell-date-muted. Today adds.cell-today(accent color + bold). Selected adds.cell-selected(background). Hover on any<td>uses--hb-calendar-hover. - Event chips:
.cell-eventbuttons, full width of the cell, stacked; default background--hb-calendar-event-button-colorand text--bulma-white-bis. Ifevent.coloris set, inlinebackground-color(and transparent border) override the default chip background.
Logic (implementation notes)
| Concern | Implementation |
|--------|------------------|
| Month navigation | changeMonth mutates date, then dispatch("changeCalendarDate", { date }). |
| Event lists | $derived filters: monthsEvent, previousMonthEvents, nextMonthEvents by M + YYYY of f.date. |
| Cell click vs chip | Cell onclick → selectDay(...); chip onclick → calendarEventClick({ eventId }). |
| id prop | Normalized in $effect to "" when falsy; used as a conventional element id hook if your layer sets it. |
Note: The $effect block contains if (typeof date === "string") dayjs(date).startOf("month").toDate(); without assigning the result to date. Prefer supplying date as a Date (or rely on your framework’s property deserialization) for a predictable visible month.
Custom element tag
hb-calendar-eventsProperties / attributes (snake_case)
From types/webcomponent.type.d.ts. In plain HTML, attributes are strings; booleans are typically yes / no in this project’s web-component conventions—and this component’s $effect accepts string forms for disable_header as described above. events should be a JSON string representing an array of event objects.
| Name | Type (authoring) | Role |
|------|------------------|------|
| id | string (optional) | Optional identifier; coerced to "" when missing in $effect. |
| date | Date (optional) | Month anchor; default start of current month. Prefer a real Date instance. |
| events | IEvent[] or JSON string | Events to show as chips; optional. Parsed from string when needed. |
| selected | Date or parseable string (optional) | Highlights the matching day (including padding months). |
| disable_header | boolean or string (optional) | When true, the entire header block (nav + slots) is omitted. |
IEvent shape (each item in events)
| Field | Required | Notes |
|-------|----------|--------|
| date | yes | Event occurrence; matched by month/year/day for chip placement. |
| label | yes | Chip button text. |
| id | yes | Passed back as eventId in calendarEventClick. |
| link | no | Typing only; not rendered. |
| icon | no | Typing only; not rendered. |
| color | no | If set, applied as inline background-color on the chip. |
Events (host CustomEvent)
All dispatched on the custom element host via new CustomEvent(name, { detail }), so by default they do not bubble and are not composed through the shadow boundary—listen on the hb-calendar-events element itself.
| Event name | detail (TypeScript) | When |
|------------|------------------------|------|
| calendarEventClick | { eventId: string } | User activates an event chip (id → eventId). |
| changeCalendarDate | { date: Date } | Visible month changes (prev/next). |
| changeSelectedDate | { selectedDate: Date } | User selects a day cell (any row, including padding). |
CSS custom properties
Documented in extra/docs.ts (styleSetup.vars); defaults in styles/webcomponent.scss fall back to Bulma tokens.
| Variable | Role |
|----------|------|
| --hb-calendar-event-button-color | Default background for event chips (default: --bulma-link). |
| --hb-calendar-cell-border | 1px cell border color (default: --bulma-border). |
| --hb-calendar-hover | <td> hover background (default: --bulma-background-lighter). |
| --hb-calendar-selected | Selected cell background (default: --bulma-info). |
| --hb-calendar-today | “Today” day number color (default: --bulma-primary). |
| --bulma-radius | Chip border radius (docs default 0.375rem; SCSS fallback 0.25rem). |
| --bulma-white-bis | Chip text on default chip background. |
| --bulma-text-weak | Muted weekday header tone. |
CSS parts (::part)
| Part | Target |
|------|--------|
| calendar-header | Outer header strip (month navigation area). |
| calendar-current-time-header | Inner span wrapping the default month/year line (and calendar_month slot). |
| cell | Each day <td> in the grid (current, padding, selected, today). |
Slots
Names and descriptions match extra/docs.ts (htmlSlots).
| Slot | Purpose |
|------|---------|
| header | Wraps default header content: replace entire header row layout while keeping the outer calendar-header part on the parent when disable_header is false. |
| calendar_month | Replace the default month name + year text inside the title span. |
| header_month_icon_prev | Replace default previous month control (default: small ˂ button). |
| header_month_icon_next | Replace default next month control (default: small ˃ button). |
Typings
Authoring types for consumers and wrappers live in:
src/wc/calendar-events/types/webcomponent.type.d.ts
IEvent— one calendar entry (date, label, id, optional link/icon/color).Component— props:id,date,events,selected,disable_header.Events— maps custom event names to theirdetailshapes:calendarEventClick,changeCalendarDate,changeSelectedDate.
After a full web-component build, generated DOM / Svelte element typings may also list this tag under types/html-elements.d.ts and types/svelte-elements.d.ts in the package output.
Minimal example
<hb-calendar-events
events='[{"id":"launch","label":"Launch","date":"2026-03-15T10:00:00.000Z"}]'
></hb-calendar-events>With explicit month anchor, selection, no header, and a listener (vanilla JS):
<hb-calendar-events
id="cal-1"
disable_header="yes"
date="2026-03-01T00:00:00.000Z"
selected="2026-03-15T00:00:00.000Z"
events='[{"id":"a","label":"Ship","date":"2026-03-15T12:00:00.000Z","color":"#2574fc"}]'
></hb-calendar-events>
<script>
const el = document.getElementById("cal-1");
el.addEventListener("changeSelectedDate", (e) => {
console.log("selected", e.detail.selectedDate);
});
el.addEventListener("calendarEventClick", (e) => {
console.log("event", e.detail.eventId);
});
</script>Adjust attribute serialization (yes/no, JSON escaping) to match how your custom element layer maps host attributes to component props.
