Fluxon.Components.DatePicker (Fluxon v1.0.20)
A powerful and flexible date picker component that provides a rich set of features for date selection. Built with LiveView integration in mind, this component offers comprehensive date handling capabilities from simple single date selection to complex date ranges with time picking.
Usage
At its simplest form, the date picker can be used standalone with just a name attribute:
<.date_picker name="appointment" label="Appointment Date" />
This renders a field button that, when clicked, opens a calendar overlay positioned below the button. The calendar starts at the current month and allows selecting any date. When a date is selected, the calendar closes automatically and the selected date is displayed in the button using the default format (e.g., "Jan 15, 2024").
Like all other form components, the date picker supports various labeling options to provide context and guidance to users:
<.date_picker
name="appointment"
label="Appointment Date"
sublabel="Required"
description="Choose when you'd like to schedule your consultation"
help_text="Our office is open Monday through Friday, 9 AM to 5 PM"
/>
Date Range Constraints
The date picker supports limiting the selectable dates through the min
and max
attributes.
Both attributes expect an Elixir Date
struct and provide client-side validation only. For
security purposes, you should always implement corresponding backend validations, typically
through Ecto changesets.
Here's an example that limits date selection to the next 30 days:
<.date_picker
field={f[:deadline]}
label="Project Deadline"
min={Date.utc_today()}
max={Date.add(Date.utc_today(), 30)}
/>
You can also use date literals with the ~D
sigil for fixed dates:
<.date_picker
name="birth_date"
label="Birth Date"
min={~D[1900-01-01]}
max={Date.utc_today()}
/>
When date constraints are set, the component:
- Disables and visually styles dates outside the allowed range
- Prevents keyboard navigation to disabled dates
- Automatically disables month/year navigation buttons when the visible month would not show any selectable dates
- Ensures the calendar opens to a month containing selectable dates, even if the current month is outside the allowed range
- Maintains the constraints when switching between months or years
- Clears the selection if a previously selected date becomes invalid due to dynamically changed constraints
Multiple Date Selection
The date picker supports selecting multiple dates through the multiple
attribute. This mode is
particularly useful for scenarios like scheduling recurring events, selecting holiday dates, or
planning multiple appointments:
<.date_picker
field={f[:holidays]}
label="Company Holidays"
multiple
description="Select all company holidays for the year"
help_text="Click dates to toggle selection"
/>
When multiple date selection is enabled:
- The calendar remains open after each selection to facilitate choosing multiple dates
- Each selected date is visually highlighted in the calendar
- Clicking a selected date deselects it
- The toggle button displays "X dates selected" when more than one date is chosen
- Time picker integration is not supported (see Time Picker Integration)
- Auto-close mode is not supported (see Closing Strategies)
Browser Behavior with Multiple Fields
When using multiple selection fields in HTML forms (e.g., name="dates[]"
), browsers have a
specific behavior that can affect form submissions:
- When options are selected, each value is included in the form data
- However, when all options are deselected, the browser omits the field entirely from the form submission
- This means the server cannot distinguish between "no form submitted" and "user deselected all dates"
To work around this browser limitation, the component automatically includes a hidden input with an empty value. As a result, when all dates are deselected, an empty string is sent to the backend instead of omitting the field entirely. This results in consistent form submissions:
# When dates are selected
%{
"date_form" => %{
"holidays" => ["2025-05-15", "2025-05-14", "2025-05-22"]
}
}
# When all dates are deselected (hidden input provides the empty value)
%{
"date_form" => %{
"holidays" => [""] # Instead of field being omitted entirely
}
}
Date Formatting
The date picker uses Elixir's strftime
under the hood to format dates. Control how dates are displayed using the display_format
attribute:
<.date_picker
field={f[:date]}
label="Date"
display_format="%B %-d, %Y" # Displays as "January 1, 2024"
/>
The format string should match the type of value being used. When using a date picker without
time selection, only date-related format specifiers are valid (e.g., %Y
, %m
, %d
).
Common date format patterns:
"%Y-%m-%d"
: ISO format (2024-01-01)"%b %-d, %Y"
: Short month (Jan 1, 2024)"%B %-d, %Y"
: Full month (January 1, 2024)"%d/%m/%Y"
: European format (01/01/2024)"%m/%d/%Y"
: US format (01/01/2024)
When using the time picker, you can include time format patterns:
<.date_picker
field={f[:appointment]}
label="Appointment"
show_time_picker
display_format="%B %-d, %Y at %I:%M %p" # January 1, 2024 at 02:30 PM
/>
Common datetime format patterns:
"%Y-%m-%d %H:%M:%S"
: ISO format with 24h time (2024-01-01 14:30:00)"%B %-d, %Y at %I:%M %p"
: Full month with 12h time (January 1, 2024 at 02:30 PM)"%d/%m/%Y %H:%M"
: European format with 24h time (01/01/2024 14:30)"%m/%d/%Y %I:%M %p"
: US format with 12h time (01/01/2024 02:30 PM)"%a, %b %-d at %I:%M %p"
: Short format (Mon, Jan 1 at 02:30 PM)
Format Specifier Compatibility
Time-related format specifiers (%H
, %M
, %S
, %I
, %p
) are only valid when
show_time_picker={true}
. Using these specifiers without enabling the time picker
will result in formatting errors.
Week Start Configuration
The date picker allows customizing which day of the week appears in the first column
through the week_start
attribute. This is particularly useful for adapting to different
cultural preferences, as some regions start their weeks on Sunday while others prefer Monday:
<.date_picker
field={f[:date]}
label="Date"
week_start={1} # Week starts on Monday
/>
The week_start
attribute accepts a number from 0 to 6, where each number maps to a day:
Integer | Weekday | Notes |
---|---|---|
0 | Sunday | Default |
1 | Monday | Common in Europe |
2 | Tuesday | |
3 | Wednesday | |
4 | Thursday | |
5 | Friday | Common in Islamic countries |
6 | Saturday | Common in Nepal |
When you change the week start day:
- The calendar grid adjusts to show the specified day in the first column
- All other days shift accordingly while maintaining their order
- Week-based keyboard navigation (e.g., up/down arrows) adapts to the new week structure
- The calendar maintains the new week start even when navigating between months
Time Picker Integration
The date picker can be enhanced with time selection capabilities through the show_time_picker
attribute. This integration changes how the component handles dates and form submissions:
<.date_picker
field={f[:appointment]}
label="Appointment"
show_time_picker
time_format="12" # or "24" for 24-hour format
/>
Field Type Requirement
When using the time picker, the field must be a NaiveDateTime
or DateTime
struct.
Using a Date
struct will result in runtime exceptions.
The time picker changes how values are handled:
- Form submissions include time information in ISO 8601 format:
%{ "appointment_form" => %{ "scheduled_appointment" => "2025-02-12 08:00:00" # Instead of just "2025-02-12" } }
- By default, times are displayed in 12-hour format with AM/PM selection
- Use
time_format="24"
to switch to 24-hour format - Time picker is not available in multiple date selection mode
- For better UX, the component automatically switches to manual close mode (see Closing Strategies)
Features and behavior:
- Separate inputs for hours and minutes
- Arrow keys (↑/↓) increment/decrement values
- Direct number input for quick value entry
- AM/PM toggle in 12-hour mode
- Keyboard navigation between time inputs
- Values are preserved when switching months/years
- Time inputs are disabled until a date is selected
- Enter key confirms the current selection
Date Range Selection
The date picker supports selecting a range of dates through the range
attribute. This mode
requires specifying both start and end fields, either through form integration or standalone
attributes:
Field Requirements
When using range mode, you must provide both start and end fields. Using only one field
or using the single field
attribute will result in runtime errors.
With Phoenix Forms (Recommended)
<.form :let={f} for={@changeset}>
<.date_picker
range
start_field={f[:start_date]}
end_field={f[:end_date]}
label="Date Range"
/>
</.form>
Standalone Usage
<.date_picker
range
start_name="start_date"
end_name="end_date"
start_value={@start_date}
end_value={@end_date}
label="Date Range"
/>
When range selection is enabled:
- The calendar remains open after selecting the first date
- Visual feedback shows the selected range:
- Start and end dates are highlighted
- Days between start and end are styled differently
- Dates are automatically sorted (earlier date becomes start)
- Clicking a selected date clears the selection
- Form submissions include two separate fields:
%{ "date_form" => %{ "start_date" => "2024-03-01", "end_date" => "2024-03-15" } }
The component supports several interaction patterns:
- Click two different dates to select a range
- Click the same date twice to create a single-day range
- Click a date within an existing range to shrink the range
- Click outside an existing range to expand it
- Click a selected date to clear the selection and start over
Closing Strategies
The date picker provides three distinct modes for handling when the calendar closes and when changes are confirmed, controlled through the close
attribute:
<.date_picker
field={f[:date]}
close="auto" # Default: closes on selection
# close="manual" # Stays open after selection
# close="confirm" # Requires explicit confirmation
/>
Auto Mode (Default)
The default "auto"
mode provides the most streamlined experience for single date selection:
- Calendar closes immediately after selecting a date
- Change events are emitted as soon as a date is selected
- Best suited for simple date picking scenarios
- Automatically falls back to manual mode when using:
- Time picker integration
- Range selection
- Multiple date selection
Manual Mode
The "manual"
mode keeps the calendar open after selection, offering more flexibility:
- Calendar stays open after date selection
- Change events are emitted immediately as selections change
- Users must explicitly close the calendar via:
- Escape key
- Clicking outside
- Clicking the toggle
Confirm Mode
The "confirm"
mode adds an explicit confirmation step:
- Adds Cancel and Confirm buttons at the bottom
- Calendar stays open until explicitly confirmed or cancelled
- Changes are treated as "pending" until confirmed
- Change events are only emitted after clicking the confirm button
- Cancelling or closing the calendar restores the previous selection
Mode Selection Tips
- Use
"auto"
for simple, single date selections - Use
"manual"
when users need to compare or review dates - Use
"confirm"
when accuracy is crucial and changes need explicit confirmation - The component will automatically adjust to
"manual"
mode when using time picker, range selection, or multiple selection features for better user experience
Navigation Modes
The date picker provides three navigation modes that determine how users can move between months and years. Each mode is configured through the navigation
attribute.
Default Mode
The default mode offers a simple and clean interface focused on month-to-month navigation. It displays the current month and year as text, with arrow buttons on either side for moving to the previous or next month.
<.date_picker field={f[:date]} navigation="default" />
Extended Mode
Extended mode enhances the default navigation by adding year navigation arrows alongside the month arrows. This creates a more efficient way to navigate through larger time spans while maintaining the familiar arrow-based interaction.
<.date_picker field={f[:date]} navigation="extended" />
Select Mode
Select mode transforms the navigation interface by combining month arrows with dropdown menus for both month and year selection. The dropdowns provide direct access to any month or year within the allowed range.
<.date_picker field={f[:date]} navigation="select" />
Navigation and Constraints
The date picker intelligently handles navigation when date constraints are set:
- Navigation buttons and dropdowns automatically disable when reaching min/max limits
- The calendar ensures the visible month always contains selectable dates
- Year dropdown options are limited to the years within the allowed range
Form Integration
The date picker component provides robust form integration through two approaches: using Phoenix form fields (recommended) or using standalone name attributes. Each approach supports single dates, multiple dates, and date ranges.
Using with Phoenix Forms (Recommended)
Use the field
attribute to bind the date picker to a form field:
<.form :let={f} for={@changeset} phx-change="validate" phx-submit="save">
<.date_picker
field={f[:appointment_date]}
label="Appointment Date"
min={Date.utc_today()}
max={Date.add(Date.utc_today(), 30)}
/>
</.form>
Using the field
attribute provides:
- Automatic value handling from form data
- Error handling and validation messages
- Form submission with correct field names
- Integration with changesets
- ID generation for accessibility
- Proper type conversion between form data and Elixir date types
- Nested form data handling
Here's a complete example showing different date field types and validations:
defmodule MyApp.Booking do
use Ecto.Schema
import Ecto.Changeset
schema "bookings" do
# Single date field
field :appointment_date, :date
# Date with time
field :meeting_datetime, :naive_datetime
# Multiple dates
field :blocked_dates, {:array, :date}
# Date range
field :start_date, :date
field :end_date, :date
timestamps()
end
def changeset(booking, attrs) do
booking
|> cast(attrs, [:appointment_date, :meeting_datetime, :blocked_dates, :start_date, :end_date])
|> validate_required([:appointment_date])
|> validate_future_date(:appointment_date)
|> validate_date_range()
end
# Custom validation for future dates
defp validate_future_date(changeset, field) do
validate_change(changeset, field, fn _, date ->
if Date.compare(date, Date.utc_today()) == :lt do
[{field, "must be in the future"}]
else
[]
end
end)
end
# Validate that end_date comes after start_date
defp validate_date_range(changeset) do
case {get_field(changeset, :start_date), get_field(changeset, :end_date)} do
{start_date, end_date} when not is_nil(start_date) and not is_nil(end_date) ->
if Date.compare(end_date, start_date) == :lt do
add_error(changeset, :end_date, "must be after start date")
else
changeset
end
_ ->
changeset
end
end
end
# In your LiveView
def mount(_params, _session, socket) do
changeset = Booking.changeset(%Booking{}, %{})
{:ok, assign(socket, form: to_form(changeset))}
end
def render(assigns) do
~H"""
<.form :let={f} for={@form} phx-change="validate">
<.date_picker
field={f[:appointment_date]}
label="Appointment Date"
min={Date.utc_today()}
/>
<.date_picker
field={f[:meeting_datetime]}
label="Meeting Time"
show_time_picker
time_format="12"
/>
<.date_picker
field={f[:blocked_dates]}
label="Blocked Dates"
multiple
help_text="Select multiple dates that should be unavailable"
/>
<.date_picker
range
start_field={f[:start_date]}
end_field={f[:end_date]}
label="Booking Period"
/>
</.form>
"""
end
def handle_event("validate", %{"booking" => params}, socket) do
changeset =
%Booking{}
|> Booking.changeset(params)
|> Map.put(:action, :validate)
{:noreply, assign(socket, form: to_form(changeset))}
end
Using Standalone Date Pickers
For simpler cases or when not using Phoenix forms, use the name
attribute:
# Single date
<.date_picker
name="filter_date"
label="Filter By Date"
value={@selected_date}
/>
# Multiple dates
<.date_picker
name="holiday_dates[]"
label="Holiday Dates"
multiple
value={@selected_dates}
/>
# Date range
<.date_picker
range
start_name="check_in"
end_name="check_out"
start_value={@check_in}
end_value={@check_out}
label="Stay Period"
/>
# Date with time
<.date_picker
name="event_time"
label="Event Time"
show_time_picker
time_format="24"
value={@event_datetime}
/>
When using standalone date pickers:
- The
name
attribute determines the form field name - Values are managed through the
value
attribute (orstart_value
/end_value
for ranges) - For multiple selection, append
[]
to the name attribute - Errors are passed via the
errors
attribute - Values are submitted in ISO 8601 format (YYYY-MM-DD or YYYY-MM-DD HH:mm:ss)
- For date ranges, use
start_name
andend_name
to specify field names
Type Handling
The date picker automatically handles conversion between string values and Elixir date types:
:date
fields expect and returnDate
structs:naive_datetime
fields expect and returnNaiveDateTime
structs:datetime
fields expect and returnDateTime
structs- Multiple selection fields work with lists of these types
- When using standalone mode, values are always in ISO 8601 string format
Keyboard Support
The component provides comprehensive keyboard navigation:
Key | Element Focus | Description |
---|---|---|
Tab /Shift+Tab | Toggle button | Moves focus to and from the date picker |
Space /Enter | Toggle button | Opens/closes the date picker |
↑ | Calendar | Moves to the same day in the previous week |
↓ | Calendar | Moves to the same day in the next week |
← | Calendar | Moves to the previous day |
→ | Calendar | Moves to the next day |
Home | Calendar | Moves to the first day of the current week |
End | Calendar | Moves to the last day of the current week |
PageUp | Calendar | Moves to the same day in the previous month |
PageDown | Calendar | Moves to the same day in the next month |
Enter /Space | Calendar | Selects the focused date |
Escape | Any | Closes the date picker |
Backspace | Toggle button | Clears selection (when clearable={true} ) |
When time picker is enabled:
Key | Element Focus | Description |
---|---|---|
↑ /↓ | Time input | Increments/decrements the value |
0-9 | Time input | Sets the value directly |
a /p | AM/PM select | Switches between AM/PM |
The component implements a focus trap when open, ensuring keyboard navigation stays within the calendar.
Summary
Components
Renders a date picker component with rich features and full keyboard navigation support.
Components
Renders a date picker component with rich features and full keyboard navigation support.
This component provides a flexible way to handle date selection, from simple single date picking to complex date ranges with time selection. It includes built-in form integration, error handling, and accessibility features.
Attributes
id
(:any
) - The unique identifier for the date picker component. When not provided, a random ID will be generated. Defaults tonil
.field
(Phoenix.HTML.FormField
) - The form field to bind to. When provided, the component automatically handles value tracking, errors, and form submission.name
(:any
) - The form name for the date picker. Required when not using thefield
attribute. For multiple selections, this will be automatically suffixed with[]
.value
(:any
) - The current selected value(s). For multiple selections, this should be a list. When using forms, this is automatically handled by thefield
attribute.range
(:boolean
) - When true, enables date range selection. This requires using eitherstart_field
/end_field
orstart_name
/end_name
attributes.Defaults to
false
.start_field
(Phoenix.HTML.FormField
) - The form field for the start date when using range selection with forms. Required whenrange={true}
and using form integration.end_field
(Phoenix.HTML.FormField
) - The form field for the end date when using range selection with forms. Required whenrange={true}
and using form integration.start_name
(:any
) - The form name for the start date when using range selection without forms. Required whenrange={true}
and not using form integration.end_name
(:any
) - The form name for the end date when using range selection without forms. Required whenrange={true}
and not using form integration.start_value
(:any
) - The current start date value when using range selection without forms.end_value
(:any
) - The current end date value when using range selection without forms.min
(Date
) - The earliest date that can be selected. Dates before this will be disabled. Defaults tonil
.max
(Date
) - The latest date that can be selected. Dates after this will be disabled. Defaults tonil
.multiple
(:boolean
) - When true, allows selecting multiple dates. This submits an array of dates. Defaults tofalse
.display_format
(:string
) - The format string used to display the selected date(s) in the toggle button. Uses strftime format. Common patterns:"%Y-%m-%d"
: ISO format (2024-01-01)"%b %-d, %Y"
: Short month (Jan 1, 2024)"%B %-d, %Y"
: Full month (January 1, 2024)"%d/%m/%Y"
: European format (01/01/2024)"%m/%d/%Y"
: US format (01/01/2024)
Defaults to
"%b %-d, %Y"
.week_start
(:integer
) - The day of the week that should appear in the first column.0
: Sunday (default)1
: Monday2
: Tuesday3
: Wednesday4
: Thursday5
: Friday6
: Saturday
Defaults to
0
.size
(:string
) - Controls the size of the date picker component:"sm"
: Small size, suitable for compact UIs"base"
: Default size, suitable for most use cases"lg"
: Large size, suitable for prominent selections"xl"
: Extra large size, suitable for hero sections
Defaults to
"base"
.class
(:any
) - Additional CSS classes to be applied to the date picker calendar container. These classes will be merged with the default styles.Defaults to
nil
.show_time_picker
(:boolean
) - When true, adds time selection fields below the calendar. This changes the value type from Date to NaiveDateTime in form submissions.Defaults to
false
.time_format
(:string
) - The time format to use whenshow_time_picker
is true:"12"
: 12-hour format with AM/PM selection"24"
: 24-hour format
Defaults to
"12"
.close
(:string
) - Controls how the date picker closes after selection:"auto"
: Closes immediately after selection (default)"manual"
: Stays open after selection"confirm"
: Requires explicit confirmation via button
Defaults to
"auto"
.navigation
(:string
) - Controls the calendar navigation interface:"default"
: Month arrows only"extended"
: Month and year arrows"select"
: Month arrows + year/month dropdowns
Defaults to
"default"
.inline
(:boolean
) - When true, renders the calendar inline instead of in a dropdown. Defaults tofalse
.disabled
(:boolean
) - When true, disables the date picker component. Disabled date pickers cannot be interacted with and appear visually muted.Defaults to
false
.label
(:string
) - The primary label for the date picker. This text is displayed above the date picker and is used for accessibility purposes.Defaults to
nil
.sublabel
(:string
) - Additional context displayed to the side of the main label. Useful for providing extra information without cluttering the main label.Defaults to
nil
.description
(:string
) - A longer description to provide more context about the date picker. This appears below the label but above the date picker element.Defaults to
nil
.help_text
(:string
) - Help text to display below the date picker. This can provide additional context or instructions for using the date picker.Defaults to
nil
.placeholder
(:string
) - Text to display when no date is selected. This text appears in the date picker toggle and helps guide users to make a selection.Defaults to
nil
.errors
(:list
) - List of error messages to display below the date picker. These are automatically handled when using thefield
attribute with form validation.Defaults to
[]
.