# `Koyomi.Presets`
[🔗](https://github.com/hattoriai/koyomi/blob/v0.2.1/lib/koyomi/presets.ex#L1)

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](filters.md)
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.

# `entry`

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

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

# `preset`

```elixir
@type preset() ::
  atom()
  | {:in | :ago, pos_integer(), atom()}
  | {:last | :next, pos_integer(), :days}
  | {String.t(),
     Date.t() | Date.Range.t() | (Date.t() -&gt; Date.t() | Date.Range.t())}
```

# `unit`

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

# `key`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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])}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
