Skip to content

KeenMate/web-daterangepicker

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

61 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Date Range Picker Web Component

A lightweight, framework-agnostic date picker that ships as a single custom element. Keyboard-first navigation, single / range / multiple selection modes, optional time + datetime modes, and OS-aware dark mode out of the box.

⚠️ Security Notice: This component intentionally allows raw HTML in rendering callbacks and message content to give developers full control over content display. If you display user-generated content, you must sanitize it yourself. See HTML Injection (XSS) Notice for the complete list of affected callbacks and methods.

What's New in v2.0.0-rc03

  • Weekday header labels — customizable at last — The picker gained a weekdayNames override (property and DatePickerOptions), closing a long-standing asymmetry with the already-overridable monthNames (issue #5). Previously weekday headers were locked to whatever Intl produced for the locale; now you can supply your own seven short labels. The array is indexed by day-of-week — [0]=Sunday … [6]=Saturday, matching Date.getDay() — and weekStartDay only rotates the display, never the mapping, so labels stay glued to the correct days at any start-of-week (Monday, Wednesday-first business weeks, whatever).

  • Declarative name overrides in plain HTML — Both month-names and weekday-names are now settable as pipe-delimited attributes (weekday-names="Ne|Po|Út|St|Čt|Pá|So"), not just via JS properties — no script needed for static localization. month-names is indexed [0]=January … [11]=December (matches Date.getMonth()). Both are dual-path: the explicitly-set property wins when both an attribute and a property are present, and changes route through the surgical updateOptions path so they re-render without a full picker rebuild.

  • Malformed name lists fail loudly, not silently — A pipe list without exactly 12 / 7 non-empty segments is now ignored (locale names used as fallback) and emits a console.warn naming the offending attribute and segment count — so a typo'd override surfaces in the console instead of silently doing nothing or crashing on a bad index.

What's New in v2.0.0-rc02

  • Scoped read-only lock — lock() / unlock() / toggleLock() — A new imperative API freezes user interaction while keeping the selected value fully readable, unlike disabled (which greys the input out and blocks the whole field). The motivating flow: the user picks a range, a custom confirm button calls the server, and on success you call lock() so the confirmed range can no longer be edited. Locks are enforced at every user-interaction choke point across date-picker.ts, date-picker-navigation.ts, date-picker-interaction.ts, and date-picker-ui.ts, so day clicks, drag-to-adjust, typed input, keyboard month-crossing, and popover re-opening all respect it.

  • Four independent lock aspects — selection, navigation, actions, openlock() with no argument freezes everything; lock(aspect | aspect[]) freezes a subset via the new exported LockAspect type. selection covers day/drag/typed/time picks and flips the <input> to readOnly; navigation covers < / >, PageUp/Down, Ctrl+arrows and the rolling year/month selector; actions covers Apply and custom buttons; open blocks re-opening the popover but never blocks closing, so a locked picker is never a keyboard trap. Partial locks compose — lock(['selection','actions']) freezes the range and buttons while leaving < / > live so a user can still browse other months after confirming.

  • Programmatic API stays live — the lock gates the end user, not your code — Selection setters (selectedRanges = …), clearSelection(), and the navigation methods all keep working while locked, exactly like a readOnly <input> is still settable from JS. This keeps server-driven flows (confirm, amend, reset) fully scriptable against a locked picker.

  • Declarative readonly attribute + greyed affordance<web-daterangepicker readonly> or picker.readonly = true applies a full lock and survives the attribute-driven rebuild; it reads back true only when every aspect is locked. Locked regions grey out like disabled controls via the new --drp-opacity-locked token (default 0.6, inherited from --drp-opacity-disabled; set to 1 to disable greying), driven by per-aspect root modifiers .drp__picker--locked-{aspect}.

What is it

@keenmate/web-daterangepicker is a date picker that runs as a Web Component — drop the <web-daterangepicker> element into any HTML page (or any framework that speaks DOM: React, Vue, Svelte, Angular, plain HTML) and it works. No JavaScript framework integration to build. No virtualized rendering. No copy-pasted theme tokens to keep in sync.

It covers the full date-picker matrix in one component: single date or date range selection; single / range / multiple modes; date, time, or datetime picker modes with four time-display UIs (rolls, Material clock face, iOS wheel, iOS compact pills); multi-month grids; input masking with progressive auto-format as the user types; drag-to-adjust for range endpoints; disabled dates with five different range-traversal strategies; special dates with badges, tooltips, and dayClass / badgeClass discriminator hooks; localized week start, month names, and labels; and async validation via beforeDateSelectCallback with optional bulk metadata loading per month.

What makes it different from the rest of the date-picker ecosystem:

  • No JavaScript dark-mode detection. The picker reacts to five separate CSS-only signals (OS prefers-color-scheme, page-level color-scheme, framework class on an ancestor, per-instance data-theme attribute, explicit light override) via light-dark() and :host-context(...) selectors. Set whichever your page already uses; it flips automatically.
  • Theme Designer integration. Optional. If you use the KeenMate Theme Designer at theme-designer.keenmate.dev, the picker reads the cross-component --base-* taxonomy so one theme drives every KeenMate component on the page.
  • One component, every selection shape. Date / range / multiple / time / datetime in one custom element with one consistent API — switch by changing one attribute, not by installing a different package.
  • First-class keyboard. Arrow keys, Home / End, Ctrl+Home / End, Tab between month columns, T to jump to today. Modal mode blurs the input so the mobile soft-keyboard collapses.

For a more honest accessibility audit including current ARIA gaps, see docs/accessibility.md.

Demos & docs

Install

npm install @keenmate/web-daterangepicker

rc releases are tagged separately — install with @rc or pin the exact version:

npm install @keenmate/web-daterangepicker@rc

Quick start

Basic HTML

<!-- Single date -->
<web-daterangepicker
  selection-mode="single"
  date-format-mask="YYYY-MM-DD"
  placeholder="Select date"
></web-daterangepicker>

<!-- Date range -->
<web-daterangepicker
  selection-mode="range"
  date-format-mask="YYYY-MM-DD"
  visible-months-count="2"
  placeholder="Select date range"
></web-daterangepicker>

With JavaScript / TypeScript

// Import the component (registers <web-daterangepicker> + injects styles)
import '@keenmate/web-daterangepicker';

const picker = document.querySelector('web-daterangepicker');

// Listen for date selection
picker.addEventListener('date-select', (e) => {
  console.log('Selected:', e.detail.formattedValue);
  console.log('Date object:', e.detail.date);
  console.log('Range:', e.detail.dateRange);
});

// Programmatic API
picker.show();
picker.hide();
picker.toggle();
picker.clearSelection();
picker.setInputValue('2025-11-15');

Using the DateRangePicker class directly (without the custom element) is fully supported — see docs/usage.md for the CSS-loading caveats.

Browser support

Modern browsers with Web Components and CSS color-mix() support:

  • Chrome / Edge 111+
  • Firefox 113+
  • Safari 16.2+

Built with BlissFramework

Follows the BlissFramework component guidelines for structure, theming, color-scheme, and accessibility. See VALIDATION-NOTES.md for the accepted-deviation register and docs/theming.md for the four-contract theming model.

HTML Injection (XSS) Notice

The following callbacks and methods allow raw HTML injection and are intentionally not XSS-safe. This gives developers full control over rendering but requires sanitizing untrusted data:

Callback / Method Output used in Risk
showMessage(html) Message area (innerHTML) HTML injection
renderDayCallback Day cells (innerHTML) HTML injection
renderDayContentCallback Day cells (innerHTML) HTML injection
getDateMetadataCallback (badgeText, dayTooltip) Badges / tooltips (innerHTML) HTML injection
formatSummaryCallback Summary display (innerHTML) HTML injection
getMonthHeaderCallback Month headers (innerHTML) HTML injection
getUnifiedHeaderCallback Unified header (innerHTML) HTML injection
customStylesCallback Style tag (textContent) CSS injection
actionButtons[].label Button labels (innerHTML) HTML injection

Safe callbacks (output is escaped or used as data):

  • beforeDateSelectCallback, beforeMonthChangedCallback (return action objects)
  • onSelect, onChange (event handlers)
  • getDateMetadataCallback (isDisabled, dayClass, badgeClass — CSS class names only)

If displaying user-generated content, sanitize it before passing it to these callbacks or methods.

Known Limitations

Consumer-data class convention — .holiday / .event / .badge-{count,number,text}

The shipped CSS includes default styles for a small set of class names the picker doesn't emit itself — they're applied by your code via the dayClassMember / badgeClassMember callbacks (or the equivalent getDateMetadataCallback return shape). The component ships compound selectors (.drp__day.holiday, .drp__day.event, .drp__badge-cell.badge-count, .drp__badge-cell.badge-number, .drp__badge-cell.badge-text) as ready-to-use hooks for these common conventions so the most frequent decoration cases work without writing any CSS.

This means a few CSS classes inside the shadow root don't follow strict BEM (.<prefix>__element--modifier) — they're consumer-data values used as discriminators next to a BEM block, not component-emitted modifiers. The BEM block (.drp__day, .drp__badge-cell) carries the component scope; the discriminator carries the data convention.

If your data uses different class names, just supply them via the callback and provide your own CSS — the defaults won't fight you. To retheme the existing defaults without rewriting the CSS, override the backing variables:

  • Holiday cell: --drp-holiday-color, --drp-holiday-bg-opacity, --drp-holiday-hover-bg-opacity
  • Event cell: --drp-event-color, --drp-event-bg-opacity, --drp-event-hover-bg-opacity
  • Badge types: --drp-badge-number-{bg,color}, --drp-badge-count-{bg,color}, --drp-badge-text-{bg,color}

These variables are part of the public theming surface (added in v1.6.0; see CHANGELOG.md).

Changelog

See CHANGELOG.md for version history and migration guides.

License

MIT — see LICENSE if present, otherwise the MIT terms apply by default per package.json.

Credits

Extracted from the Pure Admin design system.

About

Framework-agnostic date range picker with input masking, full keyboard navigation, scrollable year/month picker, multi-month side-by-side display, drag-to-adjust endpoints, and themeable with CSS custom properties

Topics

Resources

Stars

Watchers

Forks

Contributors

Languages