← Back to Examples

📦 Basic Examples

Core features including single/range modes, date formats, localization, and multi-month layouts

Basic Examples

selection_mode="single" or selection_mode="range"

Click to open calendar or type date directly
Select start and end dates with day/night count

Calendar Trigger Modes

calendar_open_trigger="focus | typing | manual"

The calendar-open-trigger attribute controls when the calendar opens. Three modes are available: focus (opens on focus), typing (opens when typing starts), and manual (opens only via button).

📌 Calendar opens when input receives focus (tab or click)
⌨️ Calendar opens when you start typing (e.g., "2025")
🔘 Calendar only opens via button click or .show()/.toggle()

💡 Trigger Modes:
focus - Opens on input focus (default behavior)
typing - Opens when user starts typing in the input
manual - Only opens via button click or programmatic calls (.show()/.toggle())
Note: Add data-calendar-button to buttons to prevent calendar from closing when clicked.

Different Date Formats

date_format_mask="DD.MM.YYYY" (also MM/DD/YYYY, …)

Try typing: 20250115 → auto-formats to 2025-01-15
Try typing: 15012025 → auto-formats to 15.01.2025
Try typing: 01152025 → auto-formats to 01/15/2025

Internationalization (i18n)

locale="de" + display_format_mask="tt.mm.jjjj"

Auto-detect user's locale or explicitly set language with built-in translations for English, German, French, and Spanish.

Automatically uses your browser's language
Spanish UI with localized format mask (aaaa = año)
German UI with localized format mask (jjjj = jahr)
French UI with localized format mask (aaaa = année)
2-month range picker with Spanish localization
German with weekends disabled (custom strings require direct PureDatePicker instantiation)

🌍 Server-driven i18n keen extra

The locale is chosen on the server. Built-in languages set the locale attribute; languages the component doesn't ship (Czech, Polish) are localized by pushing custom_strings + month_names. Every switch calls Keenmate.WebDaterangepicker.push_update/3 — no page reload, no client JS.

Current: en — the server pushed it with push_update/3. Try Czech or Polish to see custom_strings/month_names in action.
Initial render uses the declarative custom_strings/month_names attrs (applied by the hook on mount); switching re-pushes them from the server.

🖥️ Server-driven feedback (booking desk) keen extra

A booking-desk flow driven entirely from the server — the wrapper's distinct spin on v2.0.0's imperative feedback API (the Inline with Async Price demo below does the same loader/summary trick from client JS; this one does it from the LiveView, message-first). Pick a range and hit Request booking: the server shows a “checking…” note with an in-message spinner (show_message/4 + show_loader(:message)), then answers with a success or error show_message/4. No client JS runs. The 🔒 dates are held as booked on the server — the calendar only hints them; the client still lets you select across them, and the server is the authority that rejects the overlap.

Server sees your selection as — forwarded by the hook on every pick, so the server flags a 🔒 clash the moment you complete an overlapping range (no button needed). “Request booking” then spins the message block and confirms an available range with the price (€79/night) + a Ref #, then locks the picker until you “Clear”.

Multi-Month Display

visible_months_count={3}

Great for selecting dates far apart
Each month has independent navigation

Grid Layout (2×3)

month_layout="grid" grid_rows={2} grid_columns={3}

Shows 6 months in a 2×3 grid layout with responsive behavior

Pre-filled Values

💡 How this is done
Both values are set declaratively from the server (computed in mount/3, anchored to today): value="2026-07-26" and value="2026-07-26 to 2026-08-09". In a real app you'd more often bind a form field — field={@form[:stay]} — to fill the value from the server.

Disabled State

disabled={true} · value="2026-07-26"

Week Start Day

week_start_day="1" (Mon) · "0" (Sun) · "auto"

Week starts on:
Week starts on Sunday (0)
Week starts on Monday (1)

Date Restrictions

disabled_weekdays="0,6" · min_date="2026-07-01" max_date="2026-07-31" · disabled_dates={["2026-07-23", "2026-07-28", "2026-08-02", "2026-08-09"]}

Sunday (0) and Saturday (6) are disabled
Only the current month is selectable
Specific dates (relative to today) are disabled

Rolling Selector Constraints (Year/Month Picker)

rolling_year_range="2024-2026" · rolling_month_range="06-08" · current year (server): rolling_year_range="2026"

Configure which years and months appear in the rolling selector, and automatically disable unavailable options based on minDate/maxDate.

Click month/year to open rolling selector - only the current year is enabled (other years disabled)
Rolling selector only shows years 2024, 2025, 2026 (instead of ±50 years)
Rolling selector only shows June, July, August in years 2024-2026
Only the current year, only Oct/Nov/Dec visible and enabled
Years 2024-2025 available, but only November and December months selectable
Rolling selector locked to 2025 - no other years shown

Range Selection Modes (over disabled dates)

disabled_dates_handling="allow | prevent | block | split | individual"

Allows range over disabled dates. Returns enabled/disabled arrays.
Blocks range from crossing disabled dates. Snaps to last enabled date. (Sundays, Wednesdays, Saturdays disabled)
Returns multiple ranges split by disabled dates. Weekends not highlighted.
Returns flat array of individual enabled dates. Weekends not highlighted.

Week-Snapping Selection (adjustedRanges)

JS callback: beforeDateSelectCallback returns action: 'adjust' with adjustedRanges: [...] (v2.0.0 multi-range)

A beforeDateSelectCallback that rewrites the raw selection into whole weeks. For every week the selection touches it counts the selected work days (Mon–Fri): if 2 or more land in that week the entire week (Mon–Sun) is selected; fewer than 2 drops the week. The surviving weeks are returned as adjustedRanges — one range per week — so the grid, summary, and onSelect all reflect the snap.

Select Mon–Tue of a week → snaps to the full week. Select a lone Friday → dropped (0 days).

Custom Summary Formatting

JS callback (no attribute): set el.formatSummaryCallback — see demos.js

Use the formatSummaryCallback option to completely customize the summary display below the calendar.

Shows calculated price based on nights
Shows weekdays vs weekends breakdown
Shows count of separate periods (weekends excluded)
Custom format showing only nights

Inline Calendar Display

positioning_mode="inline"

Display the calendar as a static block element (not a popup).

No input field - calendar always visible
Perfect for booking widgets and dashboards
Great for business day selection
Shows holidays and events
Select dates to see traffic alerts and pricing suggestions. Uses custom-action event for message buttons.
Select a range — a spinner shows in the summary block via showLoader('summary'), then the price is written with showSummary(). Re-selecting cancels the in-flight request (AbortController).
One navigation header controls all 6 months. Click the date range to open month/year selector.
Three months side-by-side with unified controls. Individual month headers show only static month/year labels.
Navigation uses the CENTER month (May) as anchor. Click prev/next to see all months shift around it.
Click the unified header range display to open month/year selector (rolling list).
Limited to years 2026-2027 and summer months (June, July, August) with 3x3 grid layout. Tests fixed year range behavior.
Uses getUnifiedHeaderCallback to display only the center (anchor) month instead of full range.

Position Control

calendar_placement="bottom-start" (top/bottom · -start/-end)

Control where the popup appears relative to the input field.

Centered below the input
Left-aligned below the input
Right-aligned below the input
Centered above the input
Left-aligned above the input
Right-aligned above the input

Keyboard Shortcuts

  • ↑ ↓ - Navigate up/down by week
  • ← → - Navigate left/right by day
  • Ctrl+← / Ctrl+→ - Previous / next month
  • Home - First day of current month
  • End - Last day of current month
  • Ctrl+Home - January 1st of current year (repeat for previous year)
  • Ctrl+End - December 31st of current year (repeat for next year)
  • PageUp / PageDown - Previous / next month
  • Enter - Select focused day
  • Escape - Close calendar
  • Tab / Shift+Tab - Switch between month columns (multi-month mode)
  • T - Jump to today

Features

  • 🎯 Input Masking: Auto-format dates as you type
  • ⌨️ Keyboard Navigation: Full keyboard support for accessibility
  • 📅 Rolling Selector: Quick month/year selection with scrollable lists
  • 📊 Multi-Month: Show multiple months side by side
  • 🎨 Themeable: All styles use CSS custom properties (--drp-*)
  • 🖱️ Drag-to-Adjust: Drag range endpoints to adjust selection
  • 🌐 Multiple Formats: YYYY-MM-DD, DD.MM.YYYY, MM/DD/YYYY, etc.
  • 🌍 Internationalization: Built-in locales (en, de, fr, es) with auto-detection and custom strings
  • 🌍 Locale-Aware: Auto-detect week start day from user's locale
  • 🚫 Date Restrictions: Min/max dates, disabled days/dates, custom logic
  • 🎉 Special Dates: Highlight holidays, events with labels & styling
  • Modern: Web Component with Shadow DOM

Programmatic API

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

// Methods
picker.show();        // Show calendar
picker.hide();        // Hide calendar
picker.toggle();      // Toggle calendar
picker.clearSelection();       // Clear selection
picker.getInputValue();    // Get current value
picker.setInputValue('2025-11-15'); // Set value

// Properties
picker.selectionMode = 'range';
picker.dateFormatMask = 'DD.MM.YYYY';
picker.value = '2025-11-15';

// Events
picker.addEventListener('date-select', (e) => {
  console.log('Selected:', e.detail);
});

Usage

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

<script type="module">
  import '@keenmate/web-daterangepicker';

  const picker = document.querySelector('web-daterangepicker');
  picker.addEventListener('date-select', (e) => {
    console.log('Date selected:', e.detail.formattedValue);
  });
</script>
🔌 Server round-trip
0
The LiveView server saw 0 event(s) from the wrapper.
(pick a date anywhere on this page)