Date range
Required range with Until left open
Required range with From left open
Required range with either end left open
Comparing seasons
Nights from the first day to the last.
Type both days, such as 12 May and 16 May.
Month grain on Date range
Date filter on or after a day
Date filter on or before a day
Date filter between two days
Cannot be changed
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
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,
})}
/>
}