Core features including single/range modes, date formats, localization, and multi-month layouts
selection_mode="single" or selection_mode="range"
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).
💡 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.
date_format_mask="DD.MM.YYYY" (also MM/DD/YYYY, …)
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.
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.
en — the server pushed it with
push_update/3. Try Czech or Polish to see
custom_strings/month_names in action.
custom_strings/month_names
attrs (applied by the hook on mount); switching re-pushes them from the server.
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.
— — 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”.
visible_months_count={3}
month_layout="grid" grid_rows={2} grid_columns={3}
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={true} · value="2026-07-26"
week_start_day="1" (Mon) · "0" (Sun) · "auto"
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"]}
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.
disabled_dates_handling="allow | prevent | block | split | individual"
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.
JS callback (no attribute): set el.formatSummaryCallback — see demos.js
Use the formatSummaryCallback option to completely customize the summary
display below the calendar.
positioning_mode="inline"
Display the calendar as a static block element (not a popup).
custom-action event for message buttons.
showLoader('summary'), then the price is written with showSummary(). Re-selecting cancels the in-flight request (AbortController).
calendar_placement="bottom-start" (top/bottom · -start/-end)
Control where the popup appears relative to the input field.
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);
});
<!-- 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>
(pick a date anywhere on this page)