npm.io
2.0.0 • Published 1 month ago

@19h47/calendar

Licence
ISC
Version
2.0.0
Deps
0
Size
34 kB
Vulns
0
Weekly
0
Stars
1

@19h47/calendar

Lightweight date / range picker. Markup and CSS are yours — the package fills the grid and handles selection.

Installation

pnpm add @19h47/calendar

Markup

Required hooks:

Selector Role
Root element Passed to new Calendar(el, options)
.js-previous / .js-next Month navigation (optional)
.js-title Month / year label; click advances to the next month (same as .js-next)
.js-days Weekday headers row
.js-body Day cells
.js-day Day button (generated)
<div class="Calendar js-calendar">
	<header>
		<button type="button" class="js-previous">Previous</button>
		<button type="button" class="js-title" id="calendar-label" aria-live="polite"></button>
		<button type="button" class="js-next">Next</button>
	</header>
	<table role="grid" aria-labelledby="calendar-label">
		<thead>
			<tr class="js-days"></tr>
		</thead>
		<tbody class="js-body"></tbody>
	</table>
</div>

Usage

import Calendar, { type Options } from "@19h47/calendar";

const el = document.querySelector(".js-calendar") as HTMLElement;
const options: Partial<Options> = {
	locale: "fr",
	deselect: true,
	buttonClass: "btn btn-outline-primary", // optional — lib ships unstyled buttons
};

const calendar = new Calendar(el, options);
calendar.init();

el.addEventListener("Calendar.change", ({ detail }) => {
	// detail.values → string[] (`YYYY-MM-DD`)
	console.log(detail.values);
});

destroy() detaches listeners and clears the grid.

Single date
new Calendar(el, { single: true, deselect: true }).init();
Date range
new Calendar(el, { single: false }).init();
Locale & week start

locale drives labels via Intl. Week start defaults from the locale (weekInfo); pass firstDay to override.

new Calendar(el, { locale: "fr" }).init();
// fr → week starts Monday

new Calendar(el, { locale: "en", firstDay: 1 }).init();
// force Monday despite en

Update at runtime:

import Calendar, { getWeekStart } from "@19h47/calendar";

calendar.options.locale = "ja";
calendar.options.firstDay = getWeekStart("ja");
calendar.render();
Custom labels
new Calendar(el, {
	locale: "fr",
	days: ["Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam"],
	months: [
		"Janvier", "Février", "Mars", "Avril", "Mai", "Juin",
		"Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre",
	],
}).init();
Cell hook

Override renderInner to enrich or rebuild each day cell. Keep .js-day and data-day.

calendar.renderInner = (inner, date) => {
	const button = inner.querySelector(".js-day");
	if (!button) return;

	button.classList.add("MyDay");
	button.innerHTML = `<time datetime="${date.toISOString().slice(0, 10)}">${date.getDate()}</time>`;
};

Pair with custom stateClasses / header markup — see the Custom markup demo in index.html.

Options

Option Type Default Description
locale string document.documentElement.lang or "en" BCP 47 locale for title and weekday/month labels
buttonClass string "" Extra classes on day buttons (e.g. Bootstrap)
months string[] Month names override (Jan→Dec). Defaults to Intl
days string[] Weekday labels override (Sun→Sat). Defaults to Intl
single boolean true Single date vs range
firstDay number from locale Week start (0=Sun … 6=Sat)
deselect boolean false Allow clearing the selection in single mode
allowPast boolean false Allow selecting past dates
name string Forwarded in Calendar.change detail
stateClasses object see source Selection classes (active, range, start, end)

Exported types: Options, StateClasses, Current. Also exported: getWeekStart, toDayString, fromDayString.

Attributes

Attribute Description
data-month Initial month (011)
data-year Initial year
data-picked-dates JSON array of YYYY-MM-DD days to preselect, e.g. ["2023-11-05","2023-11-07"]

Accessibility

The calendar grid follows the WAI-ARIA APG date grid practices.

In your markup (source of truth): role="grid", aria-labelledby, aria-live on the title, aria-label on nav buttons, etc.

Generated by the lib:

  • Roving tabindex (one focusable day at a time)
  • aria-selected on selected days
  • aria-disabled on unavailable days (still in the keyboard grid)
  • aria-current="date" on today
  • Weekday headers with abbr (full day name)
Keyboard (focus in the grid)
Key Action
Arrow keys Move by day / week (crosses months)
Home / End First / last day of the week
Page Up / Page Down Previous / next month (same day number, or last day of month)
Shift + Page Up / Down Previous / next year
Enter / Space Select the focused day

Events

Calendar.change — fired on selection change.

detail: {
	values: string[]; // `YYYY-MM-DD`
	name?: string;
}

Live demos

  • GitHub Pages (from docs/)
  • Local: pnpm dev then open the app (see index.html)

Refresh the Pages demo after lib changes:

pnpm run docs

Acknowledgments