Koyomi.Presets (Koyomi v0.2.1)

Copy Markdown View Source

Presets: the common choices beside the calendar, such as "Today", "Last 7 days" or "In a month".

<.date_range_picker field={@form[:period]} presets={[:last_7_days, :this_month, :year_to_date]} />
<.date_picker field={@form[:due]} presets={[:today, :tomorrow, {:in, 1, :week}, {:in, 1, :month}]} />

The server computes each preset's value from today (in the picker's time zone), so a preset is the same day in the browser and on the server. A preset that the rules refuse is shown disabled.

Presets

  • :today, :tomorrow, :yesterday
  • {:in, n, unit} and {:ago, n, unit}, with unit one of :day, :week, :month, :year (or their plurals)
  • :this_week, :next_week, :last_week, :this_month, :next_month, :last_month, :this_quarter, :last_quarter, :this_year, :last_year - ranges in a range picker, their first day in a date picker
  • {:last, n, :days} and {:next, n, :days} - the last n days up to today, and the next n days from today
  • :month_to_date, :year_to_date
  • {label, value} - the app's own: a label and a Date, a Date.Range or a function of today that returns one

The browser labels the built-in presets in the picker's locale (Intl.RelativeTimeFormat and Koyomi.Messages): "Today", "Next week", "In 30 days", "Hoje", "La semaine prochaine".

Keys, and presets that stay relative

Each preset has a key: a built-in preset's own ("today", "last_7_days", "this_month", "in_1_week", "month_to_date", see key/1), or the one an entry of a keyword list gives it:

presets={[recent: {:last, 7, :days}, mtd: :month_to_date, :this_year]}

A picker with relative posts the key of the preset a person chose, instead of today's dates (see the Filters and URLs guide), and shows a key it is given as that preset. to_date/2 and to_range/2 turn a key back into the dates it covers on a day.

Summary

Types

A preset of a picker's presets: a preset, or a key and a preset.

Functions

The key of a built-in preset, or nil for an app's own {label, value} (which a keyword list names).

The preset of a built-in key ("last_7_days", "this_month", "in_1_week"), or :error. Hyphens work as underscores, and the units as their plurals.

Resolves presets for a picker kind (:date, :range, :dates, :datetime) and today, into the list that the browser reads: maps with "value" (an ISO date, or %{"from", "to"} for a range), and "label" (the app's) or "relative" / "message" (for the browser to label), and "key", the preset's key.

The day of a preset, or of its key, on today: a range preset gives its first day, as in a date picker. :error for a key that is not a built-in one.

The range of a preset, or of its key, on today: a day preset gives a range of one day, as in a range picker. Options as in to_date/2.

Types

entry()

@type entry() :: preset() | {atom(), preset()}

A preset of a picker's presets: a preset, or a key and a preset.

preset()

@type preset() ::
  atom()
  | {:in | :ago, pos_integer(), atom()}
  | {:last | :next, pos_integer(), :days}
  | {String.t(),
     Date.t() | Date.Range.t() | (Date.t() -> Date.t() | Date.Range.t())}

unit()

@type unit() :: :day | :week | :month | :quarter | :year

Functions

key(preset)

@spec key(preset()) :: String.t() | nil

The key of a built-in preset, or nil for an app's own {label, value} (which a keyword list names).

Examples

iex> Koyomi.Presets.key({:last, 7, :days})
"last_7_days"

iex> Koyomi.Presets.key({:in, 2, :weeks})
"in_2_week"

iex> Koyomi.Presets.key(:this_quarter)
"this_quarter"

parse(key)

@spec parse(String.t() | atom()) :: {:ok, preset()} | :error

The preset of a built-in key ("last_7_days", "this_month", "in_1_week"), or :error. Hyphens work as underscores, and the units as their plurals.

Examples

iex> Koyomi.Presets.parse("last_30_days")
{:ok, {:last, 30, :days}}

iex> Koyomi.Presets.parse("next-week")
{:ok, :next_week}

iex> Koyomi.Presets.parse("soon")
:error

resolve(presets, kind, today, first_day \\ 1)

@spec resolve([preset()] | nil, atom(), Date.t(), 1..7) :: [map()]

Resolves presets for a picker kind (:date, :range, :dates, :datetime) and today, into the list that the browser reads: maps with "value" (an ISO date, or %{"from", "to"} for a range), and "label" (the app's) or "relative" / "message" (for the browser to label), and "key", the preset's key.

first_day is the first day of the week (ISO, 1 is Monday), for the week presets.

Examples

iex> Koyomi.Presets.resolve([:tomorrow], :date, ~D[2027-03-15])
[%{"id" => "tomorrow", "key" => "tomorrow", "value" => "2027-03-16", "relative" => %{"n" => 1, "unit" => "day"}}]

iex> [preset] = Koyomi.Presets.resolve([{:last, 7, :days}], :range, ~D[2027-03-15])
iex> preset["value"]
%{"from" => "2027-03-09", "to" => "2027-03-15"}

to_date(preset_or_key, opts \\ [])

@spec to_date(preset() | String.t(), keyword()) :: {:ok, Date.t()} | :error

The day of a preset, or of its key, on today: a range preset gives its first day, as in a date picker. :error for a key that is not a built-in one.

Options: :today (the day the preset counts from; today in UTC by default), and :first_day (the first day of the week, ISO, for the week presets; Monday by default). Give a picker's time_zone as :today's day there: Koyomi.Rules.today/1 does it.

Examples

iex> Koyomi.Presets.to_date("yesterday", today: ~D[2027-03-15])
{:ok, ~D[2027-03-14]}

iex> Koyomi.Presets.to_date(:this_month, today: ~D[2027-03-15])
{:ok, ~D[2027-03-01]}

to_range(preset_or_key, opts \\ [])

@spec to_range(preset() | String.t(), keyword()) :: {:ok, Date.Range.t()} | :error

The range of a preset, or of its key, on today: a day preset gives a range of one day, as in a range picker. Options as in to_date/2.

Examples

iex> Koyomi.Presets.to_range("last_7_days", today: ~D[2027-03-15])
{:ok, Date.range(~D[2027-03-09], ~D[2027-03-15])}

iex> Koyomi.Presets.to_range(:last_week, today: ~D[2027-03-15], first_day: 7)
{:ok, Date.range(~D[2027-03-07], ~D[2027-03-13])}