A month calendar that picks one day or a range, and the small chip that says how near a deadline is.
date_picker/1 is drawn by a hook, so the months turn without the server. The server gives it only what it
alone knows: the day that is today in the workspace's time zone (today), the shortcuts (Today,
Friday, +7 days, End of the milestone, each with its ISO date) and the texts. Month and weekday names
come from the browser's Intl in the locale given. The choice goes into hidden inputs (name for one
day, name_from and name_to for a range), as ISO dates, and the hook fires input so phx-change hears it.
Keys: ← → move a day, ↑ ↓ a week, PageUp and PageDown a month, Home and End the week, Enter or Space
picks. Days before min are dimmed and cannot be picked, and the keyboard lands on min when today is
before it. The days are buttons in a labelled group, not a grid.
The element is phx-update="ignore" (the browser owns the month on show), so a new value from the server is
not drawn: give the picker a new id to reset it, or send it the dates without a new id:
JS.dispatch("devaction:date-picker-set", to: "#due-picker", detail: %{from: "2026-10-16", to: nil})The month title is capitalised unless capitalize is false; the arrows are the prev_icon and next_icon
slots (a typographic arrow when empty). Leave name out when something else owns the form value, as the
fields below do: the hidden inputs then have no name and are not sent.
date_input/1 and date_range/1 are the fields of a form: a text field with a mask in the order of the dates of the
language (order :dmy, :mdy or :ymd, and the separator) and a button that opens a calendar in a
DevactionUI.Popover. What is sent is a hidden input with the ISO date (name for a day, name_from and
name_to for a range); the visible text has no name. Typing fills the calendar and picking in the calendar
fills the text, and phx-change hears one input per change. The label, the description and the error are
attrs (error is the message, shown under the field and tied to it with aria-describedby), and relative
is the text on the right of the field (in 3 days), which the server computes, so it follows the value when
the form is sent back. A day that does not exist, or one before min, is marked aria-invalid as it is
typed. In a range the end cannot be before the start.
due_chip/1 compares a date with today and draws it late (red), due today (amber) or later (neutral); with
recurring it is a grey chip for a repeating deadline, with its own icon (a ↻ when the slot is empty).
Summary
Functions
Attributes
id(:string) (required)name(:string) (required) - the name of the hidden input that carries the ISO date.label(:string) (required) - the visible label of the field.value(:any) - an ISO date, aDateor nil. Defaults tonil.today(:string) (required) - today's ISO date in the workspace's time zone.min(:string) - the first ISO date that can be picked or typed. Defaults tonil.locale(:string) - the language of the month and day names in the calendar. Defaults to"en".week_start(:integer) - 0 for Sunday, 1 for Monday. Defaults to0.order(:atom) - the order of day, month and year in the text. Defaults to:dmy. Must be one of:dmy,:mdy, or:ymd.separator(:string) - the character between the parts of the text. Defaults to"/".placeholder(:string) - e.g.dd/mm/yyyy, written in the language of the person. Defaults tonil.description(:string) - help text under the field. Defaults tonil.error(:string) - the message under the field; marks it invalid. Defaults tonil.relative(:string) - text on the right of the field, e.g.in 3 days. Defaults tonil.disabled(:boolean) - Defaults tofalse.required(:boolean) - Defaults tofalse.calendar_label(:string) (required) - names the button and the panel of the calendar.previous_label(:string) (required)next_label(:string) (required)class(:any) - Defaults tonil.- Global attributes are accepted. goes to the box around the label and the field.
Slots
icon- the calendar icon at the start of the field; it is the button that opens the calendar.prev_icon- the icon of the previous month button.next_icon- the icon of the next month button.shortcut- Accepts attributes:date(:string) (required) - an ISO date.
Attributes
id(:string) (required)name(:string) - the name of the hidden inputs; without it they are not sent. Defaults tonil.mode(:atom) - Defaults to:single. Must be one of:single, or:range.value(:any) - an ISO date, or{from, to}for a range, or nil. Defaults tonil.today(:string) (required) - today's ISO date in the workspace's time zone.min(:string) - the first ISO date that can be picked. Defaults tonil.locale(:string) - Defaults to"en".week_start(:integer) - 0 for Sunday, 1 for Monday. Defaults to0.previous_label(:string) (required)next_label(:string) (required)capitalize(:boolean) - upper-case the first letter of the month title. Defaults totrue.inside_label(:string) - added to the name of the days between the ends of a range, e.g.in the range. Defaults tonil.shadow(:boolean) - the panel's shadow, for use inside a popover. Defaults tofalse.class(:any) - Defaults tonil.- Global attributes are accepted.
Slots
prev_icon- the icon of the previous month button.next_icon- the icon of the next month button.shortcut- Accepts attributes:date(:string) (required) - an ISO date.
Attributes
id(:string) (required)name(:string) (required) -name_fromandname_toare the hidden inputs.label(:string) (required) - the visible label of the pair.value(:any) -{from, to}of ISO dates orDates, or nil. Defaults tonil.today(:string) (required) - today's ISO date in the workspace's time zone.min(:string) - Defaults tonil.locale(:string) - Defaults to"en".week_start(:integer) - Defaults to0.order(:atom) - Defaults to:dmy. Must be one of:dmy,:mdy, or:ymd.separator(:string) - Defaults to"/".placeholder(:string) - the placeholder of both fields. Defaults tonil.from_label(:string) (required) - names the start field for assistive technology.to_label(:string) (required) - names the end field for assistive technology.inside_label(:string) - added to the days between the ends, in the calendar. Defaults tonil.description(:string) - Defaults tonil.error(:string) - Defaults tonil.disabled(:boolean) - Defaults tofalse.required(:boolean) - Defaults tofalse.calendar_label(:string) (required)previous_label(:string) (required)next_label(:string) (required)class(:any) - Defaults tonil.- Global attributes are accepted.
Slots
icon- the calendar icon at the start of the first field; it opens the calendar.arrow- between the two fields; a→when empty.prev_iconnext_iconshortcut- Accepts attributes:date(:string) (required) - an ISO date.
Attributes
date(:any) - aDate; not needed for a recurring chip. Defaults tonil.today(:any) - aDate: today in the workspace's time zone; not needed for a recurring chip. Defaults tonil.text(:string) (required) - what the chip says, already in the person's language.recurring(:boolean) - a grey chip for a repeating deadline. Defaults tofalse.class(:any) - Defaults tonil.- Global attributes are accepted.
Slots
icon- the icon of a recurring chip; a↻when empty.
An ISO date (or a Date) as the text of a date field: 16/10/2026 for :dmy and "/", nothing for nil.
iex> DevactionUI.DatePicker.format_date("2026-10-16", :mdy, "/")
"10/16/2026"
:late, :today or :later.