kit

Date range

Built revision v17.10.0

React
Topics

Human support

Bring a missing role, layout, or state, or a rendering against the standard, to the session working on kit. Name the package and the entry.

On List, Reports

Date range

Period
Compare
Required range with Until left open
Period
From 1 September 2026
Required range with From left open
Period
Until 30 September 2026
Required range with either end left open
Period
Comparing seasons
Period
Compare
Stay
Nights from the first day to the last.
Trip
12 May – end of MayType both days, such as 12 May and 16 May.
Month grain on Date range
Period
Compare
Date filter on or after a day
Issued
Date filter on or before a day
Issued
Date filter between two days
Issued
Cannot be changed
Period
Compare
Only the owner can change this.
Source
@module({from: "kit/react/locales/en.json", with: {type_: "json"}})
external en: KitReact.Locale.t = "default"

let reader = Specimen_Reader.reader
let american = KitReact.Reader.make(~locale=en, ~zone="America/New_York")
let on = (reader, year, month, day) => KitReact.Time.day(reader, {year, month, day})
let span = (reader, first, last) => KitReact.Time.dayRange(reader, ~first, ~last)

let refusal = (committed: KitReact.Roles.DateRange.committed) =>
  switch committed {
  | Unresolved(_) => Some(React.string("Type both days, such as 12 May and 16 May."))
  | Empty | Span(_) | MonthSpan(_) | From(_) | Until(_) => None
  }

module Seasons = {
  @react.component
  let make = () => {
    let (value, setValue) = React.useState(() => None)
    let dates = (year, firstMonth, firstDay, lastMonth, lastDay) => span(reader, {year, month: firstMonth, day: firstDay}, {year, month: lastMonth, day: lastDay})
    <KitReact.Roles.FormGroup id="react-range-seasons" label={React.string("Period")}  kind={KitReact.Roles.DateRange.controlSet({
      name: "season", reader, value: ?value, currentPreset: ?(value->Option.isNone ? Some("autumn") : None), today: on(reader, 2026, 12, 18), direction: Backward, compare: PreviousPeriod,
      presets: [
        {key: "autumn", name: "Autumn season", span: dates(2026, 9, 1, 12, 18), comparisons: {previousPeriod: dates(2026, 4, 20, 7, 10), previousYear: dates(2025, 9, 8, 12, 20)}},
        {key: "summer", name: "Summer season", span: dates(2026, 4, 20, 7, 10), comparisons: {previousPeriod: dates(2026, 1, 12, 3, 27), previousYear: dates(2025, 4, 21, 7, 18)}},
      ],
      onCommit: given => setValue(_ => Some(given)),
    })} />
  }
}

module DateFilter = {
  @react.component
  let make = (~id) => {
    let (value, setValue) = React.useState((): KitReact.Roles.DateRange.committed => Empty)
    let words = KitReact.Roles.DateRange.valueWords(reader, value)
    <KitReact.ListFilter id={"react-date-filter-" ++ id} name="Issued" applied=?{words == "" ? None : Some([KitReact.Words.Text(words)])} clear={ {href: "./"} } control={
      <KitReact.Roles.FormGroup id={"react-filter-range-" ++ id} label={React.string("Issued")}  kind={KitReact.Roles.DateRange.controlSet({name: "issued_" ++ id, reader, value, openEnds: #either, presets: [], today: on(reader, 2026, 9, 15), direction: Either, onCommit: given => setValue(_ => given)})} />
    } />
  }
}

@react.component
let make = () => {
  let (trip, setTrip) = React.useState((): KitReact.Roles.DateRange.committed => Unresolved({first: "12 May", last: "end of May"}))
  <>
    <KitReact.Roles.FormGroup
      id="react-range-period"
      label={React.string("Period")}
      kind={KitReact.Roles.DateRange.controlSet({
        name: "period",
        reader,
        currentPreset: "last_30_days",
        today: on(reader, 2025, 4, 9),
        direction: Backward,
        last: on(reader, 2025, 4, 9),
        compare: PreviousPeriod,
      })}
    />
    {[("last", "Required range with Until left open", #last, KitReact.Roles.DateRange.From(on(reader, 2026, 9, 1))), ("first", "Required range with From left open", #first, KitReact.Roles.DateRange.Until(on(reader, 2026, 9, 30))), ("either", "Required range with either end left open", #either, KitReact.Roles.DateRange.Empty)]->Array.map(((id, name, openEnds, value)) =>
      <KitReact.Roles.FormGroup id={"react-range-required-" ++ id} label={React.string("Period")}  required=true kind={KitReact.Roles.DateRange.controlSet({name: "required_" ++ id, reader, value, openEnds, presets: [], today: on(reader, 2026, 9, 15), direction: Either})} />
    )->React.array}
    <Specimen_Case name="Comparing seasons"> <Seasons /> </Specimen_Case>
    <KitReact.Roles.FormGroup
      id="react-range-stay" printedValue={<time dateTime="2025-11-26/2025-11-30"> {KitReact.TimeParts.elements(["November 26 – ", "30, 2025"])} </time>}
      label={React.string("Stay")}
      help={React.string("Nights from the first day to the last.")}
      kind={KitReact.Roles.DateRange.controlSet({
        name: "stay",
        reader: american,
        value: Span({dates: span(american, {year: 2025, month: 11, day: 26}, {year: 2025, month: 11, day: 30}), preset: None, compare: None}),
        presets: [
          {key: "this_weekend", name: "This weekend", span: span(american, {year: 2025, month: 4, day: 12}, {year: 2025, month: 4, day: 13})},
          {key: "next_week", name: "Next week", span: span(american, {year: 2025, month: 4, day: 14}, {year: 2025, month: 4, day: 20})},
        ],
        today: on(american, 2025, 4, 9),
        direction: Forward,
        first: on(american, 2025, 4, 9),
      })}
    />
    <KitReact.Roles.FormGroup
      id="react-range-trip" printedValue={React.string("12 May – end of May")}
      label={React.string("Trip")}
      error=?{refusal(trip)}
      kind={KitReact.Roles.DateRange.controlSet({
        name: "trip",
        reader,
        value: trip,
        presets: [],
        today: on(reader, 2025, 4, 9),
        direction: Forward,
        first: on(reader, 2025, 4, 9),
        onCommit: committed => setTrip(_ => committed),
      })}
    />
    <KitReact.Roles.FormGroup id="react-month-range" label={React.string("Period")}  kind={KitReact.Roles.DateRange.controlSet({name: "month_period", reader, grain: #month, compare: PreviousPeriod, today: on(reader, 2026, 9, 15), direction: Either})} />
    {[("after", "Date filter on or after a day"), ("before", "Date filter on or before a day"), ("between", "Date filter between two days")]->Array.map(((id, name)) => <Specimen_Case key=id name> <DateFilter id /> </Specimen_Case>)->React.array}
    <KitReact.Roles.FormGroup
      id="react-range-period-disabled"
      label={React.string("Period")}
      help={React.string("Only the owner can change this.")}
      kind={KitReact.Roles.DateRange.controlSet({name: "period", reader, disabled: true, currentPreset: "last_30_days", today: on(reader, 2025, 4, 9), direction: Backward, last: on(reader, 2025, 4, 9), compare: PreviousPeriod})}
    />
  </>
}

Date range, open

Show it open

Source
@react.component
let make = () => {
  let reader = Specimen_Reader.reader
  let on = (year, month, day) => KitReact.Time.day(reader, {year, month, day})
  <KitReact.Roles.FormGroup
    id="react-range-monday"
    label={React.string("Period")}
    kind={KitReact.Roles.DateRange.controlSet({
      name: "period",
      reader,
      currentPreset: "last_30_days",
      today: on(2025, 4, 9),
      direction: Backward,
      last: on(2025, 4, 9),
      compare: PreviousPeriod,
    })}
  />
}