howdy/ui/calendar
Calendars and date pickers: one date, a range, or several dates, over one month or a few side by side.
calendar.new("due", year: 2026, month: 9)
|> calendar.selected(Some(calendar.Date(2026, 9, 24)))
|> calendar.today(calendar.Date(2026, 9, 24))
|> calendar.disabled(fn(date) { calendar.weekday(date) >= 6 })
|> calendar.navigation(
previous: [event.on_click(PreviousMonth)],
next: [event.on_click(NextMonth)],
)
|> calendar.name("due")
|> calendar.view
Clicking a day selects it; the arrow keys move a day or a week, Home
and End go to the ends of the week, and Page Up and Page Down press the
previous and next month buttons. The choice is kept in the hidden input
called name, so it submits with a form and a live view hears it with
live.on_value:
- one date, with
selected, as2026-09-24; - a range, with
range, as2026-09-10/2026-09-14: the first click picks the start and the second the end; - several dates, with
multiple, as2026-09-10,2026-09-14: each click adds or removes a day.
months shows several months side by side, and locale names the
months, weekdays and buttons in another language. Changing month is the
server’s job: navigation takes the attributes of the previous and
next buttons, such as click handlers in a live view. On a page without
a live view, a native <input type="date"> usually serves better for a
single date.
picker puts the calendar in a popover behind a button that shows the
choice.
Types
A day in the proleptic Gregorian calendar. Months and days count from 1.
pub type Date {
Date(year: Int, month: Int, day: Int)
}
Constructors
-
Date(year: Int, month: Int, day: Int)
The words a calendar uses. weekday_names and weekday_abbreviations
run from Monday; heading writes a month and year, given the year and
the month, as at the top of the calendar; date_label writes a date out
in full; day_number writes the number on each day; first_day is the
day weeks start on, from 1 for Monday to 7 for Sunday.
english is the default. howdy/ui/words holds one of these for its
locale, with names and formats from CLDR.
pub type Locale {
Locale(
weekday_names: List(String),
weekday_abbreviations: List(String),
heading: fn(Int, Int) -> String,
date_label: fn(Date) -> String,
day_number: fn(Int) -> String,
first_day: Int,
previous_month: String,
next_month: String,
choose_date: String,
)
}
Constructors
-
Locale( weekday_names: List(String), weekday_abbreviations: List(String), heading: fn(Int, Int) -> String, date_label: fn(Date) -> String, day_number: fn(Int) -> String, first_day: Int, previous_month: String, next_month: String, choose_date: String, )
What is chosen.
pub type Selection {
Single(option.Option(Date))
Range(from: option.Option(Date), to: option.Option(Date))
Multiple(List(Date))
}
Constructors
-
Single(option.Option(Date)) -
Range(from: option.Option(Date), to: option.Option(Date))A start and, once chosen, an end.
-
Multiple(List(Date))
Values
pub fn add_months(date: Date, months: Int) -> Date
The same day in the month months later, or the last day of that month
when it is shorter.
pub fn caption_class() -> css.Class
pub fn compare(a: Date, b: Date) -> order.Order
pub fn dates_from_value(value: String) -> List(Date)
Read several dates as their hidden input sends them. Anything that is not a date is left out.
pub fn days_in_month(year: Int, month: Int) -> Int
pub fn disabled(
calendar: Calendar(msg),
disabled: fn(Date) -> Bool,
) -> Calendar(msg)
Days that cannot be chosen, such as those in the past.
pub fn first_day(
calendar: Calendar(msg),
day: Int,
) -> Calendar(msg)
Start weeks on this day, from 1 for Monday to 7 for Sunday, whatever the locale’s custom.
pub fn from_iso(value: String) -> Result(Date, Nil)
Read YYYY-MM-DD, as a hidden input or <input type="date"> sends it.
pub fn grid_class() -> css.Class
pub fn heading_class() -> css.Class
pub fn is_leap_year(year: Int) -> Bool
pub fn locale(
calendar: Calendar(msg),
locale: Locale,
) -> Calendar(msg)
Name the months, weekdays and buttons in another language, and start
weeks on the locale’s first day unless first_day says otherwise.
pub fn months(
calendar: Calendar(msg),
count: Int,
) -> Calendar(msg)
Show count months side by side, starting with the calendar’s month.
pub fn months_class() -> css.Class
pub fn multiple(
calendar: Calendar(msg),
dates: List(Date),
) -> Calendar(msg)
Choose any number of dates; these ones so far.
pub fn name(
calendar: Calendar(msg),
name: String,
) -> Calendar(msg)
Keep the choice in a hidden input with this name.
pub fn nav_button_class() -> css.Class
pub fn navigation(
calendar: Calendar(msg),
previous previous: List(attribute.Attribute(msg)),
next next: List(attribute.Attribute(msg)),
) -> Calendar(msg)
Show previous and next month buttons with these attributes.
pub fn new(
id: String,
year year: Int,
month month: Int,
) -> Calendar(msg)
A calendar showing month of year. id must be unique in the
document.
pub fn picker(
calendar: Calendar(msg),
placeholder placeholder: String,
) -> element.Element(msg)
A button showing the choice, or placeholder, that opens the calendar
in a popover. Choosing one date, or the end of a range, closes it. Give
the calendar a name so the choice is kept.
pub fn picker_class() -> css.Class
pub fn popover_class() -> css.Class
pub fn range(
calendar: Calendar(msg),
from from: option.Option(Date),
to to: option.Option(Date),
) -> Calendar(msg)
Choose a range; this one, as far as it is chosen.
pub fn range_from_value(
value: String,
) -> Result(#(Date, option.Option(Date)), Nil)
Read a range as its hidden input sends it: 2026-09-10/2026-09-14, or
2026-09-10/ while only the start is chosen.
pub fn root_class() -> css.Class
pub fn selected(
calendar: Calendar(msg),
date: option.Option(Date),
) -> Calendar(msg)
Choose one date; this one, if there is one.
pub fn selection_to_value(selection: Selection) -> String
The hidden input’s value for a selection.
pub fn sunday_first(calendar: Calendar(msg)) -> Calendar(msg)
Start weeks on Sunday, whatever the locale’s custom.
pub fn today(
calendar: Calendar(msg),
date: Date,
) -> Calendar(msg)
Today, which is marked. The server’s today may not be the user’s, so pass the date in the user’s time zone when you know it.
pub fn trigger_class() -> css.Class
pub fn weekday(date: Date) -> Int
The day of the week, from 1 for Monday to 7 for Sunday.
pub fn weekday_class() -> css.Class