kit

Calendar

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.

Calendar

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let newBooking =
  Calendar.PrimaryAction({children: React.string("New booking")})

let hour = (hour): DeskGrid.hour => {time: KitReact.Time.clock(Specimen_Reader.reader, {hour, minute: 0}), props: {}}

let day = day => KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day})

let span = (on, start, end) =>
  KitReact.Time.interval(
    Specimen_Reader.reader,
    ~start=Date.fromString(`2025-04-${Int.toString(on)}T${start}:00+01:00`),
    ~end=Date.fromString(`2025-04-${Int.toString(on)}T${end}:00+01:00`),
  )

let head = (on): DeskGrid.head => {key: KitReact.Time.datetime(day(on)), column: DeskGrid.Day(day(on)), props: {}}

let placement = (row, span) => (
  {gridRow: `${Int.toString(row)} / span ${Int.toString(span)}`}: ReactDOM.Style.t
)
let part = (row, span, at) =>
  ReactDOM.Style.unsafeAddProp(placement(row, span), "--kit-calendar-block-part", Int.toString(at))
let parts = count =>
  ReactDOM.Style.unsafeAddProp({}, "--kit-calendar-column-parts", Int.toString(count))
let sized = (row, span, size) =>
  ReactDOM.Style.unsafeAddProp(placement(row, span), "--kit-calendar-block-size", size)

let hourHeight = "var(--kit-hour-height)"

let keyCollection = KitReact.Roles.Row.Unsaved([KitReact.Words.Text("This booking will move when you are back online.")])

let item = (~key, ~name, ~span, ~kind, ~room=?, ~standings=[], ~standingWords=[]): Calendar.itemRow => {
  key,
  name,
  href: Some("../../record/"),
  span,
  kind,
  standings,
  standingWords,
  room,
  state: None,
  props: {},
}

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../layout-calendar/#layout-calendar"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Calendar controls"}}
      range={
        days: KitReact.Time.week(Specimen_Reader.reader, {year: 2025, month: 4, day: 14}),
        today: {label: "Today", props: {}},
        previous: {label: "Previous week", href: "?week=previous", props: {rel: "prev"}},
        next: {label: "Next week", href: "?week=next", props: {rel: "next"}},
        date: {group: {id: "calendar-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: Specimen_Reader.reader, value: Day(KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14})), today: KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14}), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-scope-form-group", kind: Select,
          label: React.string("Hall"),
          labelProps: {htmlFor: "calendar-scope"},
          controlProps: {id: "calendar-scope"},
          controlValue: "oakmere",
          options: [
            {label: React.string("All halls"), value: "all", props: {}},
            {label: React.string("Oakmere Hall"), value: "oakmere", props: {}},
          ],
        }),
        Filter({
          id: "calendar-room-filter",
          name: "Room",
          applied: [KitReact.Words.Text("Main Hall")],
          clear: {href: "?week=this&hall=oakmere&payment=overdue&catering=kitchen"},
          control: <KitReact.Roles.FormGroup
            id="calendar-scope-room-form-group"
            kind=Select
            label={React.string("Room")}
            labelProps={{htmlFor: "calendar-scope-room"}}
            controlProps={{id: "calendar-scope-room"}}
            controlValue="main-hall"
            options=[
              {label: React.string("All rooms"), value: "all", props: {}},
              {label: React.string("Main Hall"), value: "main-hall", props: {}},
              {label: React.string("Meeting Room"), value: "meeting-room", props: {}},
              {label: React.string("Memorial Room"), value: "memorial-room", props: {}},
            ]
          />,
        }),
        Filter({
          id: "calendar-booking-filter",
          name: "Booking",
          control: <KitReact.Roles.FormGroup
            id="calendar-scope-booking-form-group"
            kind=Select
            label={React.string("Booking")}
            labelProps={{htmlFor: "calendar-scope-booking"}}
            controlProps={{id: "calendar-scope-booking"}}
            controlValue="all"
            options=[
              {label: React.string("Any booking"), value: "all", props: {}},
              {label: React.string("Regular"), value: "regular", props: {}},
              {label: React.string("One-off"), value: "one-off", props: {}},
            ]
          />,
        }),
        Filter({
          id: "calendar-status-filter",
          name: "Status",
          control: <KitReact.Roles.FormGroup
            id="calendar-scope-status-form-group"
            kind=Select
            label={React.string("Status")}
            labelProps={{htmlFor: "calendar-scope-status"}}
            controlProps={{id: "calendar-scope-status"}}
            controlValue="all"
            options=[
              {label: React.string("Any status"), value: "all", props: {}},
              {label: React.string("Confirmed"), value: "confirmed", props: {}},
              {label: React.string("Provisional"), value: "provisional", props: {}},
            ]
          />,
        }),
        Filter({
          id: "calendar-hirer-kind-filter",
          name: "Hirer kind",
          control: <KitReact.Roles.FormGroup
            id="calendar-scope-hirer-kind-form-group"
            kind=Select
            label={React.string("Hirer kind")}
            labelProps={{htmlFor: "calendar-scope-hirer-kind"}}
            controlProps={{id: "calendar-scope-hirer-kind"}}
            controlValue="all"
            options=[
              {label: React.string("Any hirer"), value: "all", props: {}},
              {label: React.string("School"), value: "school", props: {}},
              {label: React.string("Club"), value: "club", props: {}},
              {label: React.string("Private"), value: "private", props: {}},
            ]
          />,
        }),
        Filter({
          id: "calendar-payment-filter",
          name: "Payment",
          applied: [KitReact.Words.Text("Overdue")],
          clear: {href: "?week=this&hall=oakmere&room=main-hall&catering=kitchen"},
          control: <KitReact.Roles.FormGroup
            id="calendar-scope-payment-form-group"
            kind=Select
            label={React.string("Payment")}
            labelProps={{htmlFor: "calendar-scope-payment"}}
            controlProps={{id: "calendar-scope-payment"}}
            controlValue="overdue"
            options=[
              {label: React.string("Any payment"), value: "all", props: {}},
              {label: React.string("Paid"), value: "paid", props: {}},
              {label: React.string("Due"), value: "due", props: {}},
              {label: React.string("Overdue"), value: "overdue", props: {}},
            ]
          />,
        }),
        Filter({
          id: "calendar-setup-filter",
          name: "Setup",
          control: <KitReact.Roles.FormGroup
            id="calendar-scope-setup-form-group"
            kind=Select
            label={React.string("Setup")}
            labelProps={{htmlFor: "calendar-scope-setup"}}
            controlProps={{id: "calendar-scope-setup"}}
            controlValue="all"
            options=[
              {label: React.string("Any setup"), value: "all", props: {}},
              {label: React.string("Theatre"), value: "theatre", props: {}},
              {label: React.string("Cabaret"), value: "cabaret", props: {}},
              {label: React.string("Boardroom"), value: "boardroom", props: {}},
            ]
          />,
        }),
        Filter({
          id: "calendar-catering-filter",
          name: "Catering",
          applied: [KitReact.Words.Text("Kitchen")],
          clear: {href: "?week=this&hall=oakmere&room=main-hall&payment=overdue"},
          control: <KitReact.Roles.FormGroup
            id="calendar-scope-catering-form-group"
            kind=Select
            label={React.string("Catering")}
            labelProps={{htmlFor: "calendar-scope-catering"}}
            controlProps={{id: "calendar-scope-catering"}}
            controlValue="kitchen"
            options=[
              {label: React.string("Any catering"), value: "all", props: {}},
              {label: React.string("Kitchen"), value: "kitchen", props: {}},
              {label: React.string("None"), value: "none", props: {}},
            ]
          />,
        }),
        Filter({
          id: "calendar-access-filter",
          name: "Access",
          control: <KitReact.Roles.FormGroup
            id="calendar-scope-access-form-group"
            kind=Select
            label={React.string("Access")}
            labelProps={{htmlFor: "calendar-scope-access"}}
            controlProps={{id: "calendar-scope-access"}}
            controlValue="all"
            options=[
              {label: React.string("Any access"), value: "all", props: {}},
              {label: React.string("Step-free"), value: "step-free", props: {}},
              {label: React.string("Parking"), value: "parking", props: {}},
            ]
          />,
        }),
        Filter({
          id: "calendar-deposit-filter",
          name: "Deposit",
          control: <KitReact.Roles.FormGroup
            id="calendar-scope-deposit-form-group"
            kind=Select
            label={React.string("Deposit")}
            labelProps={{htmlFor: "calendar-scope-deposit"}}
            controlProps={{id: "calendar-scope-deposit"}}
            controlValue="all"
            options=[
              {label: React.string("Any deposit"), value: "all", props: {}},
              {label: React.string("Held"), value: "held", props: {}},
              {label: React.string("Returned"), value: "returned", props: {}},
            ]
          />,
        }),
        More({
          id: "calendar-more-filters",
          count: {reader: Specimen_Reader.reader, count: 0, counts: "filters", forms: {one: "filter"}},
          filters: [
            {id: "calendar-room-filter", name: "Room", applied: [KitReact.Words.Text("Main Hall")], clear: {href: "?week=this&hall=oakmere&payment=overdue&catering=kitchen"}},
            {id: "calendar-booking-filter", name: "Booking"},
            {id: "calendar-status-filter", name: "Status"},
            {id: "calendar-hirer-kind-filter", name: "Hirer kind"},
            {id: "calendar-payment-filter", name: "Payment", applied: [KitReact.Words.Text("Overdue")], clear: {href: "?week=this&hall=oakmere&room=main-hall&catering=kitchen"}},
            {id: "calendar-setup-filter", name: "Setup"},
            {id: "calendar-catering-filter", name: "Catering", applied: [KitReact.Words.Text("Kitchen")], clear: {href: "?week=this&hall=oakmere&room=main-hall&payment=overdue"}},
            {id: "calendar-access-filter", name: "Access"},
            {id: "calendar-deposit-filter", name: "Deposit"},
          ],
        }),
      ]
      filtersCount={reader: Specimen_Reader.reader, count: 3, counts: "filters", forms: {one: "filter"}}
      clearFilters={href: "?week=this&hall=oakmere"}
      view={<KitReact.Roles.FormGroup id="calendar-view-form-group" label={React.string("View")} kind={KitReact.Roles.SegmentedControl.controlSet({name: "calendar-view", value: "week", options: Three({value: "week", content: React.string("Week")}, {value: "day", content: React.string("Day")}, {value: "month", content: React.string("Month")})})} />}
      actions={content: [newBooking], statusLines: [{children: React.string("New bookings wait for the hall manager to approve them.")}]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[
            head(14),
            head(15),
            head(16),
            head(17),
            head(18),
            head(19),
            head(20),
          ]
          hours=[
            hour(8),
            hour(9),
            hour(10),
            hour(11),
            hour(12),
            hour(13),
            hour(14),
            hour(15),
            hour(16),
            hour(17),
          ]
          columns=[
            {
              key: "2025-04-14",
              children: <>
                <Calendar.Block
                  name={React.string("Board meeting")}
                  href="../../record/"
                  span={span(14, "09:00", "10:00")}
                  kind="Meeting"
                  props={{ariaLabel: "Board meeting, 09:00–10:00", style: placement(2, 1)}}
                />
                <Calendar.Block
                  name={React.string("Community lunch")}
                  href="../../record/"
                  span={span(14, "13:00", "13:29")}
                  kind="Booking"
                  props={{ariaLabel: "Community lunch, 13:00–13:29", style: sized(6, 1, `calc(${hourHeight} * 29 / 60)`)}}
                />
              </>,
              props: {},
            },
            {
              key: "2025-04-15",
              children: <>
                <Calendar.Block
                  name={React.string("School booking for Oakmere Hall")}
                  href="../../record/"
                  span={span(15, "09:00", "11:00")}
                  kind="Booking"
                  props={{ariaLabel: "School booking for Oakmere Hall, 09:00–11:00", style: placement(2, 2)}}
                />
                <Calendar.Block
                  name={React.string("Room check")}
                  href="../../record/"
                  span={span(15, "13:00", "13:30")}
                  kind="Inspection"
                  props={{ariaLabel: "Room check, 13:00–13:30", style: sized(6, 1, `calc(${hourHeight} / 2)`)}}
                />
                <Calendar.Block
                  name={React.string("Key handover")}
                  href="../../record/"
                  span={span(15, "15:00", "15:20")}
                  kind="Booking"
                  props={{ariaLabel: "Key handover, 15:00–15:20", style: sized(8, 1, `calc(${hourHeight} / 3)`)}}
                />
                <Calendar.FoldedRun
                  span={span(15, "16:00", "16:35")}
                  count={[KitReact.Words.Text("3 inspections")]}
                  href="day-one-column/"
                  props={{style: sized(9, 1, `calc(${hourHeight} * 7 / 12)`)}}
                />
                <Calendar.CurrentTime
                  props={{
                    style: ReactDOM.Style.unsafeAddProp(
                      {},
                      "--kit-calendar-now-offset",
                      `calc(3.5 * ${hourHeight})`,
                    ),
                  }}
                />
              </>,
              props: {},
            },
            {
              key: "2025-04-16",
              children: <>
                <Calendar.Block
                  name={React.string("Parish council")}
                  href="../../record/"
                  span={span(16, "11:00", "12:00")}
                  kind="Meeting"
                  props={{ariaLabel: "Parish council, 11:00–12:00", style: placement(4, 1)}}
                />
                <Calendar.Block
                  name={React.string("Meter reading")}
                  href="../../record/"
                  span={span(16, "13:00", "13:05")}
                  kind="Inspection"
                  props={{ariaLabel: "Meter reading, 13:00–13:05", style: sized(6, 1, `calc(${hourHeight} / 12)`)}}
                />
              </>,
              props: {},
            },
            {key: "2025-04-17", children: React.null, props: {}},
            {
              key: "2025-04-18",
              children: <>
                <Calendar.Block
                  name={React.string("Wedding reception")}
                  href="../../record/"
                  span={span(18, "12:00", "15:00")}
                  kind="Booking"
                  props={{ariaLabel: "Wedding reception, 12:00–15:00", style: part(5, 3, 1)}}
                />
                <Calendar.Block
                  name={React.string("Hire request")}
                  href="../../record/"
                  span={span(18, "11:00", "13:00")}
                  kind="Requested"
                  standings=[Uncovered]
                  standingWords=["Cover needed"]
                  props={{ariaLabel: "Hire request, 11:00–13:00, Cover needed", style: part(4, 2, 2)}}
                />
              </>,
              props: {style: parts(2)},
            },
            {
              key: "2025-04-19",
              children: <>
                <Calendar.Block
                  name={React.string("Yoga class")}
                  href="../../record/"
                  span={span(19, "10:00", "12:00")}
                  kind="Booking"
                  props={{ariaLabel: "Yoga class, 10:00–12:00", style: placement(3, 2)}}
                />
                <Calendar.Block
                  name={React.string("Piano lesson")}
                  href="../../record/"
                  span={span(19, "14:00", "14:30")}
                  kind="Booking"
                  standings=[Cancelled]
                  standingWords=["Cancelled"]
                  props={{ariaLabel: "Piano lesson, 14:00–14:30, Cancelled", style: sized(7, 1, `calc(${hourHeight} / 2)`)}}
                />
                <Calendar.Block
                  name={React.string("Key collection")}
                  href="../../record/"
                  span={span(19, "15:00", "15:30")}
                  kind="Booking"
                  standings=[Uncovered]
                  standingWords=["Cover needed"]
                  standing=keyCollection
                  props={{ariaLabel: "Key collection, 15:00–15:30, Cover needed, This booking will move when you are back online", style: sized(8, 1, `calc(${hourHeight} / 2)`)}}
                />
              </>,
              props: {},
            },
            {
              key: "2025-04-20",
              children: <Calendar.Block
                name={React.string("Summer term choir")}
                href="../../record/"
                span={span(20, "10:00", "12:00")}
                kind="Booking"
                standings=[Preparation, Uncovered]
                standingWords=["Season not confirmed", "Cover needed"]
                props={{ariaLabel: "Summer term choir, 10:00–12:00, Season not confirmed, Cover needed", style: placement(3, 2)}}
              />,
              props: {},
            },
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day
            day={day(14)}
            items=[
              item(
                ~key="bk-4812",
                ~name="Board meeting",
                ~span=span(14, "09:00", "10:00"),
                ~kind="Meeting",
              ),
              item(
                ~key="bk-4813",
                ~name="Community lunch",
                ~span=span(14, "13:00", "13:29"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(15)}
            items=[
              item(
                ~key="bk-4820",
                ~name="School booking for Oakmere Hall",
                ~span=span(15, "09:00", "11:00"),
                ~kind="Booking",
              ),
              item(
                ~key="bk-4821",
                ~name="Room check",
                ~span=span(15, "13:00", "13:30"),
                ~kind="Inspection",
                ~room="Meeting Room",
              ),
              item(
                ~key="bk-4822",
                ~name="Key handover",
                ~span=span(15, "15:00", "15:20"),
                ~kind="Booking",
                ~room="Meeting Room",
              ),
              item(
                ~key="bk-4823",
                ~name="Room check",
                ~span=span(15, "16:00", "16:15"),
                ~kind="Inspection",
                ~room="Meeting Room",
              ),
              item(
                ~key="bk-4825",
                ~name="Fire door check",
                ~span=span(15, "16:15", "16:30"),
                ~kind="Inspection",
                ~room="Meeting Room",
                ~standings=[Uncovered],
                ~standingWords=["Cover needed"],
              ),
              item(
                ~key="bk-4824",
                ~name="Meter reading",
                ~span=span(15, "16:30", "16:35"),
                ~kind="Inspection",
                ~room="Meeting Room",
              ),
            ]
          />
          <Calendar.Day
            day={day(16)}
            items=[
              item(
                ~key="bk-4830",
                ~name="Parish council",
                ~span=span(16, "11:00", "12:00"),
                ~kind="Meeting",
              ),
              item(
                ~key="in-4831",
                ~name="Meter reading",
                ~span=span(16, "13:00", "13:05"),
                ~kind="Inspection",
                ~room="Meeting Room",
              ),
            ]
          />
          <Calendar.Day
            day={day(17)}
            empty={<p> {React.string("Nothing booked")} </p>}
          />
          <Calendar.Day
            day={day(18)}
            items=[
              item(
                ~key="bk-4861",
                ~name="Hire request",
                ~span=span(18, "11:00", "13:00"),
                ~kind="Requested",
                ~standings=[Uncovered],
                ~standingWords=["Cover needed"],
              ),
              item(
                ~key="bk-4841",
                ~name="Wedding reception",
                ~span=span(18, "12:00", "15:00"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(19)}
            items=[
              item(
                ~key="bk-4850",
                ~name="Yoga class",
                ~span=span(19, "10:00", "12:00"),
                ~kind="Booking",
              ),
              item(
                ~key="bk-4851",
                ~name="Piano lesson",
                ~span=span(19, "14:00", "14:30"),
                ~kind="Booking",
                ~standings=[Cancelled],
                ~standingWords=["Cancelled"],
              ),
              {
                ...item(
                  ~key="bk-4852",
                  ~name="Key collection",
                  ~span=span(19, "15:00", "15:30"),
                  ~kind="Booking",
                  ~standings=[Uncovered],
                  ~standingWords=["Cover needed"],
                ),
                standing: keyCollection,
              },
            ]
          />
          <Calendar.Day
            day={day(20)}
            items=[
              item(
                ~key="bk-4871",
                ~name="Summer term choir",
                ~span=span(20, "10:00", "12:00"),
                ~kind="Booking",
                ~standings=[Preparation, Uncovered],
                ~standingWords=["Season not confirmed", "Cover needed"],
              ),
            ]
          />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, long-hours

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let hour = (hour): DeskGrid.hour => {time: KitReact.Time.clock(Specimen_Reader.reader, {hour, minute: 0}), props: {}}

let day = day => KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day})

let span = (on, start, end) =>
  KitReact.Time.interval(
    Specimen_Reader.reader,
    ~start=Date.fromString(`2025-04-${Int.toString(on)}T${start}:00+01:00`),
    ~end=Date.fromString(`2025-04-${Int.toString(on)}T${end}:00+01:00`),
  )

let head = (on): DeskGrid.head => {key: KitReact.Time.datetime(day(on)), column: DeskGrid.Day(day(on)), props: {}}

let placement = (row, span) => (
  {gridRow: `${Int.toString(row)} / span ${Int.toString(span)}`}: ReactDOM.Style.t
)
let sized = (row, span, size) =>
  ReactDOM.Style.unsafeAddProp(placement(row, span), "--kit-calendar-block-size", size)
let offsetSized = (row, span, offset, size) =>
  ReactDOM.Style.unsafeAddProp(sized(row, span, size), "--kit-calendar-block-offset", offset)

let hourHeight = "var(--kit-hour-height)"

let item = (~key, ~name, ~span, ~kind, ~room=?): Calendar.itemRow => {
  key,
  name,
  href: Some("../../../record/"),
  span,
  kind,
  standings: [],
  standingWords: [],
  room,
  state: None,
  props: {},
}

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-long-hours"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-long-hours-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Calendar controls"}}
      range={
        days: KitReact.Time.dayRange(Specimen_Reader.reader, ~first={year: 2025, month: 4, day: 14}, ~last={year: 2025, month: 4, day: 20}),
        today: {label: "Today", props: {}},
        previous: {label: "Previous week", href: "?week=previous", props: {rel: "prev"}},
        next: {label: "Next week", href: "?week=next", props: {rel: "next"}},
        date: {group: {id: "calendar-long-hours-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: Specimen_Reader.reader, value: Day(KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14})), today: KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14}), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-long-hours-scope-form-group",
          kind: Select,
          label: React.string("Hall"),
          labelProps: {htmlFor: "calendar-long-hours-scope"},
          controlProps: {id: "calendar-long-hours-scope"},
          options: [
            {label: React.string("All halls"), value: "all", props: {}},
            {label: React.string("Oakmere Hall"), value: "oakmere", props: {}},
          ],
        }),
        FormGroup({id: "calendar-long-hours-room-form-group",
          kind: Select,
          label: React.string("Room"),
          labelProps: {htmlFor: "calendar-long-hours-room"},
          controlProps: {id: "calendar-long-hours-room"},
          options: [
            {label: React.string("All rooms"), value: "all", props: {}},
            {label: React.string("Main Hall"), value: "main-hall", props: {}},
            {label: React.string("Meeting Room"), value: "meeting-room", props: {}},
          ],
        }),
      ]
      view={<KitReact.Roles.FormGroup id="calendar-long-hours-view-form-group" label={React.string("View")} kind={KitReact.Roles.SegmentedControl.controlSet({name: "calendar-long-hours-view", value: "week", options: Three({value: "week", content: React.string("Week")}, {value: "day", content: React.string("Day")}, {value: "month", content: React.string("Month")})})} />}
      actions={content: [PrimaryAction({children: React.string("New booking")})]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[
            head(14),
            head(15),
            head(16),
            head(17),
            head(18),
            head(19),
            head(20),
          ]
          hours=[
            hour(7),
            hour(8),
            hour(9),
            hour(10),
            hour(11),
            hour(12),
            hour(13),
            hour(14),
            hour(15),
            hour(16),
            hour(17),
            hour(18),
            hour(19),
            hour(20),
            hour(21),
            hour(22),
          ]
          columns=[
            {
              key: "2025-04-14",
              children: <>
                <Calendar.Block
                  name={React.string("Toddler group")}
                  href="../../../record/"
                  span={span(14, "09:30", "11:30")}
                  kind="Booking"
                  props={{ariaLabel: "Toddler group, 09:30–11:30",
                    style: offsetSized(
                      3,
                      3,
                      `calc(${hourHeight} / 2)`,
                      `calc(${hourHeight} * 2)`,
                    ),
                  }}
                />
                <Calendar.Block
                  name={React.string("Badminton club")}
                  href="../../../record/"
                  span={span(14, "19:00", "21:00")}
                  kind="Booking"
                  props={{ariaLabel: "Badminton club, 19:00–21:00", style: placement(13, 2)}}
                />
              </>,
              props: {},
            },
            {
              key: "2025-04-15",
              children: <>
                <Calendar.Block
                  name={React.string("Art class")}
                  href="../../../record/"
                  span={span(15, "10:00", "12:00")}
                  kind="Booking"
                  props={{ariaLabel: "Art class, 10:00–12:00", style: placement(4, 2)}}
                />
                <Calendar.Block
                  name={React.string("Choir rehearsal")}
                  href="../../../record/"
                  span={span(15, "18:30", "20:00")}
                  kind="Booking"
                  props={{ariaLabel: "Choir rehearsal, 18:30–20:00",
                    style: offsetSized(
                      12,
                      2,
                      `calc(${hourHeight} / 2)`,
                      `calc(${hourHeight} * 1.5)`,
                    ),
                  }}
                />
                <Calendar.CurrentTime
                  props={{
                    style: ReactDOM.Style.unsafeAddProp(
                      {},
                      "--kit-calendar-now-offset",
                      `calc(7.5 * ${hourHeight})`,
                    ),
                  }}
                />
              </>,
              props: {},
            },
            {
              key: "2025-04-16",
              children: <>
                <Calendar.Block
                  name={React.string("Lunch club")}
                  href="../../../record/"
                  span={span(16, "13:00", "15:00")}
                  kind="Booking"
                  props={{ariaLabel: "Lunch club, 13:00–15:00", style: placement(7, 2)}}
                />
                <Calendar.Block
                  name={React.string("Parish council")}
                  href="../../../record/"
                  span={span(16, "19:30", "21:30")}
                  kind="Meeting"
                  props={{ariaLabel: "Parish council, 19:30–21:30",
                    style: offsetSized(
                      13,
                      3,
                      `calc(${hourHeight} / 2)`,
                      `calc(${hourHeight} * 2)`,
                    ),
                  }}
                />
              </>,
              props: {},
            },
            {
              key: "2025-04-17",
              children: <Calendar.Block
                name={React.string("Brownies")}
                href="../../../record/"
                span={span(17, "17:30", "19:00")}
                kind="Booking"
                props={{ariaLabel: "Brownies, 17:30–19:00",
                  style: offsetSized(
                    11,
                    2,
                    `calc(${hourHeight} / 2)`,
                    `calc(${hourHeight} * 1.5)`,
                  ),
                }}
              />,
              props: {},
            },
            {
              key: "2025-04-18",
              children: <>
                <Calendar.Block
                  name={React.string("Hall inspection")}
                  href="../../../record/"
                  span={span(18, "09:00", "10:00")}
                  kind="Inspection"
                  props={{ariaLabel: "Hall inspection, 09:00–10:00", style: placement(3, 1)}}
                />
                <Calendar.Block
                  name={React.string("Quiz night")}
                  href="../../../record/"
                  span={span(18, "20:00", "22:00")}
                  kind="Booking"
                  props={{ariaLabel: "Quiz night, 20:00–22:00", style: placement(14, 2)}}
                />
              </>,
              props: {},
            },
            {
              key: "2025-04-19",
              children: <Calendar.Block
                name={React.string("Wedding reception")}
                href="../../../record/"
                span={span(19, "11:00", "15:00")}
                kind="Booking"
                props={{ariaLabel: "Wedding reception, 11:00–15:00", style: placement(5, 4)}}
              />,
              props: {},
            },
            {
              key: "2025-04-20",
              children: <>
                <Calendar.Block
                  name={React.string("Morning service")}
                  href="../../../record/"
                  span={span(20, "08:00", "09:30")}
                  kind="Booking"
                  props={{ariaLabel: "Morning service, 08:00–09:30", style: sized(2, 2, `calc(${hourHeight} * 1.5)`)}}
                />
                <Calendar.Block
                  name={React.string("Birthday party")}
                  href="../../../record/"
                  span={span(20, "16:00", "18:00")}
                  kind="Booking"
                  props={{ariaLabel: "Birthday party, 16:00–18:00", style: placement(10, 2)}}
                />
              </>,
              props: {},
            },
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day
            day={day(14)}
            items=[
              item(
                ~key="bk-6101",
                ~name="Toddler group",
                ~span=span(14, "09:30", "11:30"),
                ~kind="Booking",
              ),
              item(
                ~key="bk-6102",
                ~name="Badminton club",
                ~span=span(14, "19:00", "21:00"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(15)}
            items=[
              item(
                ~key="bk-6103",
                ~name="Art class",
                ~span=span(15, "10:00", "12:00"),
                ~kind="Booking",
              ),
              item(
                ~key="bk-6104",
                ~name="Choir rehearsal",
                ~span=span(15, "18:30", "20:00"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(16)}
            items=[
              item(
                ~key="bk-6105",
                ~name="Lunch club",
                ~span=span(16, "13:00", "15:00"),
                ~kind="Booking",
              ),
              item(
                ~key="bk-6106",
                ~name="Parish council",
                ~span=span(16, "19:30", "21:30"),
                ~kind="Meeting",
              ),
            ]
          />
          <Calendar.Day
            day={day(17)}
            items=[
              item(
                ~key="bk-6107",
                ~name="Brownies",
                ~span=span(17, "17:30", "19:00"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(18)}
            items=[
              item(
                ~key="bk-6108",
                ~name="Hall inspection",
                ~span=span(18, "09:00", "10:00"),
                ~kind="Inspection",
                ~room="Main Hall",
              ),
              item(
                ~key="bk-6109",
                ~name="Quiz night",
                ~span=span(18, "20:00", "22:00"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(19)}
            items=[
              item(
                ~key="bk-6110",
                ~name="Wedding reception",
                ~span=span(19, "11:00", "15:00"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(20)}
            items=[
              item(
                ~key="bk-6111",
                ~name="Morning service",
                ~span=span(20, "08:00", "09:30"),
                ~kind="Booking",
              ),
              item(
                ~key="bk-6112",
                ~name="Birthday party",
                ~span=span(20, "16:00", "18:00"),
                ~kind="Booking",
              ),
            ]
          />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, early-now

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let hour = (hour): DeskGrid.hour => {time: KitReact.Time.clock(Specimen_Reader.reader, {hour, minute: 0}), props: {}}

let day = day => KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day})

let span = (on, start, end) =>
  KitReact.Time.interval(
    Specimen_Reader.reader,
    ~start=Date.fromString(`2025-04-${Int.toString(on)}T${start}:00+01:00`),
    ~end=Date.fromString(`2025-04-${Int.toString(on)}T${end}:00+01:00`),
  )

let head = (on): DeskGrid.head => {key: KitReact.Time.datetime(day(on)), column: DeskGrid.Day(day(on)), props: {}}

let placement = (row, span) => (
  {gridRow: `${Int.toString(row)} / span ${Int.toString(span)}`}: ReactDOM.Style.t
)
let sized = (row, span, size) =>
  ReactDOM.Style.unsafeAddProp(placement(row, span), "--kit-calendar-block-size", size)
let offsetSized = (row, span, offset, size) =>
  ReactDOM.Style.unsafeAddProp(sized(row, span, size), "--kit-calendar-block-offset", offset)

let hourHeight = "var(--kit-hour-height)"

let item = (~key, ~name, ~span, ~kind, ~room=?): Calendar.itemRow => {
  key,
  name,
  href: Some("../../../record/"),
  span,
  kind,
  standings: [],
  standingWords: [],
  room,
  state: None,
  props: {},
}

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-early-now"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-early-now-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Calendar controls"}}
      range={
        days: KitReact.Time.dayRange(Specimen_Reader.reader, ~first={year: 2025, month: 4, day: 14}, ~last={year: 2025, month: 4, day: 20}),
        today: {label: "Today", props: {}},
        previous: {label: "Previous week", href: "?week=previous", props: {rel: "prev"}},
        next: {label: "Next week", href: "?week=next", props: {rel: "next"}},
        date: {group: {id: "calendar-early-now-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: Specimen_Reader.reader, value: Day(KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14})), today: KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14}), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-early-now-scope-form-group",
          kind: Select,
          label: React.string("Hall"),
          labelProps: {htmlFor: "calendar-early-now-scope"},
          controlProps: {id: "calendar-early-now-scope"},
          options: [
            {label: React.string("All halls"), value: "all", props: {}},
            {label: React.string("Oakmere Hall"), value: "oakmere", props: {}},
          ],
        }),
        FormGroup({id: "calendar-early-now-room-form-group",
          kind: Select,
          label: React.string("Room"),
          labelProps: {htmlFor: "calendar-early-now-room"},
          controlProps: {id: "calendar-early-now-room"},
          options: [
            {label: React.string("All rooms"), value: "all", props: {}},
            {label: React.string("Main Hall"), value: "main-hall", props: {}},
            {label: React.string("Meeting Room"), value: "meeting-room", props: {}},
          ],
        }),
      ]
      view={<KitReact.Roles.FormGroup id="calendar-early-now-view-form-group" label={React.string("View")} kind={KitReact.Roles.SegmentedControl.controlSet({name: "calendar-early-now-view", value: "week", options: Three({value: "week", content: React.string("Week")}, {value: "day", content: React.string("Day")}, {value: "month", content: React.string("Month")})})} />}
      actions={content: [PrimaryAction({children: React.string("New booking")})]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[
            head(14),
            head(15),
            head(16),
            head(17),
            head(18),
            head(19),
            head(20),
          ]
          hours=[
            hour(7),
            hour(8),
            hour(9),
            hour(10),
            hour(11),
            hour(12),
            hour(13),
            hour(14),
            hour(15),
            hour(16),
            hour(17),
            hour(18),
            hour(19),
            hour(20),
            hour(21),
          ]
          columns=[
            {
              key: "2025-04-14",
              children: <>
                <Calendar.Block
                  name={React.string("Slimming group")}
                  href="../../../record/"
                  span={span(14, "09:30", "11:00")}
                  kind="Booking"
                  props={{ariaLabel: "Slimming group, 09:30–11:00",
                    style: offsetSized(
                      3,
                      2,
                      `calc(${hourHeight} / 2)`,
                      `calc(${hourHeight} * 1.5)`,
                    ),
                  }}
                />
                <Calendar.Block
                  name={React.string("Youth club")}
                  href="../../../record/"
                  span={span(14, "18:00", "20:00")}
                  kind="Booking"
                  props={{ariaLabel: "Youth club, 18:00–20:00", style: placement(12, 2)}}
                />
              </>,
              props: {},
            },
            {
              key: "2025-04-15",
              children: <>
                <Calendar.Block
                  name={React.string("Baby clinic")}
                  href="../../../record/"
                  span={span(15, "09:00", "12:00")}
                  kind="Booking"
                  props={{ariaLabel: "Baby clinic, 09:00–12:00", style: placement(3, 3)}}
                />
                <Calendar.Block
                  name={React.string("Table tennis")}
                  href="../../../record/"
                  span={span(15, "19:00", "20:30")}
                  kind="Booking"
                  props={{ariaLabel: "Table tennis, 19:00–20:30", style: sized(13, 2, `calc(${hourHeight} * 1.5)`)}}
                />
              </>,
              props: {},
            },
            {
              key: "2025-04-16",
              children: <>
                <Calendar.Block
                  name={React.string("Coffee morning")}
                  href="../../../record/"
                  span={span(16, "10:00", "11:30")}
                  kind="Booking"
                  props={{ariaLabel: "Coffee morning, 10:00–11:30", style: sized(4, 2, `calc(${hourHeight} * 1.5)`)}}
                />
                <Calendar.Block
                  name={React.string("Trustees meeting")}
                  href="../../../record/"
                  span={span(16, "14:00", "15:30")}
                  kind="Meeting"
                  props={{ariaLabel: "Trustees meeting, 14:00–15:30", style: sized(8, 2, `calc(${hourHeight} * 1.5)`)}}
                />
                <Calendar.CurrentTime
                  props={{
                    style: ReactDOM.Style.unsafeAddProp(
                      {},
                      "--kit-calendar-now-offset",
                      `calc(2.25 * ${hourHeight})`,
                    ),
                  }}
                />
              </>,
              props: {},
            },
            {
              key: "2025-04-17",
              children: <Calendar.Block
                name={React.string("Craft club")}
                href="../../../record/"
                span={span(17, "13:00", "15:00")}
                kind="Booking"
                props={{ariaLabel: "Craft club, 13:00–15:00", style: placement(7, 2)}}
              />,
              props: {},
            },
            {
              key: "2025-04-18",
              children: <>
                <Calendar.Block
                  name={React.string("Hall deep clean")}
                  href="../../../record/"
                  span={span(18, "08:00", "09:00")}
                  kind="Inspection"
                  props={{ariaLabel: "Hall deep clean, 08:00–09:00", style: placement(2, 1)}}
                />
                <Calendar.Block
                  name={React.string("Film night")}
                  href="../../../record/"
                  span={span(18, "19:00", "21:00")}
                  kind="Booking"
                  props={{ariaLabel: "Film night, 19:00–21:00", style: placement(13, 2)}}
                />
              </>,
              props: {},
            },
            {
              key: "2025-04-19",
              children: <Calendar.Block
                name={React.string("Christening party")}
                href="../../../record/"
                span={span(19, "12:00", "16:00")}
                kind="Booking"
                props={{ariaLabel: "Christening party, 12:00–16:00", style: placement(6, 4)}}
              />,
              props: {},
            },
            {
              key: "2025-04-20",
              children: <Calendar.Block
                name={React.string("Art exhibition")}
                href="../../../record/"
                span={span(20, "11:00", "15:00")}
                kind="Booking"
                props={{ariaLabel: "Art exhibition, 11:00–15:00", style: placement(5, 4)}}
              />,
              props: {},
            },
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day
            day={day(14)}
            items=[
              item(
                ~key="bk-6201",
                ~name="Slimming group",
                ~span=span(14, "09:30", "11:00"),
                ~kind="Booking",
              ),
              item(
                ~key="bk-6202",
                ~name="Youth club",
                ~span=span(14, "18:00", "20:00"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(15)}
            items=[
              item(
                ~key="bk-6203",
                ~name="Baby clinic",
                ~span=span(15, "09:00", "12:00"),
                ~kind="Booking",
              ),
              item(
                ~key="bk-6204",
                ~name="Table tennis",
                ~span=span(15, "19:00", "20:30"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(16)}
            items=[
              item(
                ~key="bk-6205",
                ~name="Coffee morning",
                ~span=span(16, "10:00", "11:30"),
                ~kind="Booking",
              ),
              item(
                ~key="bk-6206",
                ~name="Trustees meeting",
                ~span=span(16, "14:00", "15:30"),
                ~kind="Meeting",
              ),
            ]
          />
          <Calendar.Day
            day={day(17)}
            items=[
              item(
                ~key="bk-6207",
                ~name="Craft club",
                ~span=span(17, "13:00", "15:00"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(18)}
            items=[
              item(
                ~key="bk-6208",
                ~name="Hall deep clean",
                ~span=span(18, "08:00", "09:00"),
                ~kind="Inspection",
                ~room="Main Hall",
              ),
              item(
                ~key="bk-6209",
                ~name="Film night",
                ~span=span(18, "19:00", "21:00"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(19)}
            items=[
              item(
                ~key="bk-6210",
                ~name="Christening party",
                ~span=span(19, "12:00", "16:00"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(20)}
            items=[
              item(
                ~key="bk-6211",
                ~name="Art exhibition",
                ~span=span(20, "11:00", "15:00"),
                ~kind="Booking",
              ),
            ]
          />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, bands

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let hour = (hour): DeskGrid.hour => {time: KitReact.Time.clock(Specimen_Reader.reader, {hour, minute: 0}), props: {}}

let day = day => KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day})

let span = (on, start, end) =>
  KitReact.Time.interval(
    Specimen_Reader.reader,
    ~start=Date.fromString(`2025-04-${Int.toString(on)}T${start}:00+01:00`),
    ~end=Date.fromString(`2025-04-${Int.toString(on)}T${end}:00+01:00`),
  )

let head = (on): DeskGrid.head => {key: KitReact.Time.datetime(day(on)), column: DeskGrid.Day(day(on)), props: {}}

let band = (name, first, last, stack): DeskGrid.band => {
  name: React.string(name),
  href: Some("../../../record/"),
  first,
  last,
  stack,
  props: {},
}

let placement = (row, span) => ({gridRow: `${Int.toString(row)} / span ${Int.toString(span)}`}: ReactDOM.Style.t)
let sized = (row, span, size) =>
  ReactDOM.Style.unsafeAddProp(placement(row, span), "--kit-calendar-block-size", size)

let bandRow = (key, name): Calendar.bandRow => {
  key,
  name: React.string(name),
  href: Some("../../../record/"),
  state: None,
  props: {},
}

let item = (~key, ~name, ~span, ~kind): Calendar.itemRow => {
  key,
  name,
  href: Some("../../../record/"),
  span,
  kind,
  standings: [],
  standingWords: [],
  room: None,
  state: None,
  props: {},
}

let nothing = <p> {React.string("Nothing booked")} </p>

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-bands"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-bands-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Calendar controls"}}
      range={
        days: KitReact.Time.dayRange(Specimen_Reader.reader, ~first={year: 2025, month: 4, day: 14}, ~last={year: 2025, month: 4, day: 18}),
        today: {label: "Today", props: {}},
        previous: {label: "Previous week", href: "?week=previous", props: {rel: "prev"}},
        next: {label: "Next week", href: "?week=next", props: {rel: "next"}},
        date: {group: {id: "calendar-bands-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: Specimen_Reader.reader, value: Day(KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14})), today: KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14}), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-bands-scope-form-group",
          kind: Select,
          label: React.string("Hall"),
          labelProps: {htmlFor: "calendar-bands-scope"},
          controlProps: {id: "calendar-bands-scope"},
          options: [
            {label: React.string("All halls"), value: "all", props: {}},
            {label: React.string("Oakmere Hall"), value: "oakmere", props: {}},
          ],
        }),
        FormGroup({id: "calendar-bands-room-form-group",
          kind: Select,
          label: React.string("Room"),
          labelProps: {htmlFor: "calendar-bands-room"},
          controlProps: {id: "calendar-bands-room"},
          options: [
            {label: React.string("All rooms"), value: "all", props: {}},
            {label: React.string("Main Hall"), value: "main-hall", props: {}},
          ],
        }),
      ]
      view={<KitReact.Roles.FormGroup id="calendar-bands-view-form-group" label={React.string("View")} kind={KitReact.Roles.SegmentedControl.controlSet({name: "calendar-bands-view", value: "week", options: Three({value: "week", content: React.string("Week")}, {value: "day", content: React.string("Day")}, {value: "month", content: React.string("Month")})})} />}
      actions={content: [PrimaryAction({children: React.string("New booking")})]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[
            head(14),
            head(15),
            head(16),
            head(17),
            head(18),
          ]
          bands=[
            band("Hall closed for rewiring", 2, 4, 1),
            band("Spring fair", 5, 5, 1),
            band("Oakmere Primary School summer fair rehearsal", 5, 5, 2),
          ]
          hours=[
            hour(9),
            hour(10),
            hour(11),
            hour(12),
            hour(13),
          ]
          columns=[
            {
              key: "2025-04-14",
              children: <Calendar.Block
                name={React.string("Board meeting")}
                href="../../../record/"
                span={span(14, "09:00", "10:00")}
                kind="Meeting"
                props={{ariaLabel: "Board meeting, 09:00–10:00", style: placement(1, 1)}}
              />,
              props: {},
            },
            {
              key: "2025-04-15",
              children: <Calendar.Block
                name={React.string("Yoga class")}
                href="../../../record/"
                span={span(15, "11:00", "12:00")}
                kind="Booking"
                props={{ariaLabel: "Yoga class, 11:00–12:00", style: placement(3, 1)}}
              />,
              props: {},
            },
            {key: "2025-04-16", children: React.null, props: {}},
            {key: "2025-04-17", children: React.null, props: {}},
            {
              key: "2025-04-18",
              children: <>
                <Calendar.Block
                  name={React.string("Choir rehearsal")}
                  href="../../../record/"
                  span={span(18, "10:00", "11:30")}
                  kind="Booking"
                  props={{ariaLabel: "Choir rehearsal, 10:00–11:30", style: sized(2, 2, "calc(var(--kit-hour-height) * 1.5)")}}
                />
                <Calendar.Block
                  name={React.string("Hall handover")}
                  href="../../../record/"
                  span={span(18, "13:00", "14:00")}
                  kind="Booking"
                  props={{ariaLabel: "Hall handover, 13:00–14:00", style: placement(5, 1)}}
                />
              </>,
              props: {},
            },
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day
            day={day(14)}
            items=[
              item(
                ~key="bk-5901",
                ~name="Board meeting",
                ~span=span(14, "09:00", "10:00"),
                ~kind="Meeting",
              ),
            ]
          />
          <Calendar.Day
            day={day(15)}
            bands=[bandRow("cl-118", "Hall closed for rewiring")]
            items=[
              item(
                ~key="bk-5902",
                ~name="Yoga class",
                ~span=span(15, "11:00", "12:00"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(16)}
            bands=[bandRow("cl-118", "Hall closed for rewiring")]
            empty=nothing
          />
          <Calendar.Day
            day={day(17)}
            bands=[bandRow("cl-118", "Hall closed for rewiring")]
            empty=nothing
          />
          <Calendar.Day
            day={day(18)}
            bands=[
              bandRow("bk-5905", "Spring fair"),
              bandRow("bk-5906", "Oakmere Primary School summer fair rehearsal"),
            ]
            items=[
              item(
                ~key="bk-5903",
                ~name="Choir rehearsal",
                ~span=span(18, "10:00", "11:30"),
                ~kind="Booking",
              ),
              item(
                ~key="bk-5904",
                ~name="Hall handover",
                ~span=span(18, "13:00", "14:00"),
                ~kind="Booking",
              ),
            ]
          />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, partial-day-closure

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let hour = (hour): DeskGrid.hour => {time: KitReact.Time.clock(Specimen_Reader.reader, {hour, minute: 0}), props: {}}

let day = day => KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day})

let span = (on, start, end) =>
  KitReact.Time.interval(
    Specimen_Reader.reader,
    ~start=Date.fromString(`2025-04-${Int.toString(on)}T${start}:00+01:00`),
    ~end=Date.fromString(`2025-04-${Int.toString(on)}T${end}:00+01:00`),
  )

let head = (on): DeskGrid.head => {key: KitReact.Time.datetime(day(on)), column: DeskGrid.Day(day(on)), props: {}}

let placement = (row, span) => ({gridRow: `${Int.toString(row)} / span ${Int.toString(span)}`}: ReactDOM.Style.t)
let sized = (row, span, size) =>
  ReactDOM.Style.unsafeAddProp(placement(row, span), "--kit-calendar-block-size", size)

let item = (~key, ~name, ~span, ~kind): Calendar.itemRow => {
  key,
  name,
  href: Some("../../../record/"),
  span,
  kind,
  standings: [],
  standingWords: [],
  room: None,
  state: None,
  props: {},
}

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-partial-day-closure"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-closure-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Calendar controls"}}
      range={
        days: KitReact.Time.dayRange(Specimen_Reader.reader, ~first={year: 2025, month: 4, day: 14}, ~last={year: 2025, month: 4, day: 16}),
        today: {label: "Today", props: {}},
        previous: {label: "Previous week", href: "?week=previous", props: {rel: "prev"}},
        next: {label: "Next week", href: "?week=next", props: {rel: "next"}},
        date: {group: {id: "calendar-partial-day-closure-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: Specimen_Reader.reader, value: Day(KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14})), today: KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14}), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-closure-scope-form-group",
          kind: Select,
          label: React.string("Hall"),
          labelProps: {htmlFor: "calendar-closure-scope"},
          controlProps: {id: "calendar-closure-scope"},
          options: [
            {label: React.string("All halls"), value: "all", props: {}},
            {label: React.string("Oakmere Hall"), value: "oakmere", props: {}},
          ],
        }),
        FormGroup({id: "calendar-closure-room-form-group",
          kind: Select,
          label: React.string("Room"),
          labelProps: {htmlFor: "calendar-closure-room"},
          controlProps: {id: "calendar-closure-room"},
          options: [
            {label: React.string("All rooms"), value: "all", props: {}},
            {label: React.string("Main Hall"), value: "main-hall", props: {}},
            {label: React.string("Meeting Room"), value: "meeting-room", props: {}},
          ],
        }),
      ]
      view={<KitReact.Roles.FormGroup id="calendar-partial-day-closure-view-form-group" label={React.string("View")} kind={KitReact.Roles.SegmentedControl.controlSet({name: "calendar-partial-day-closure-view", value: "week", options: Three({value: "week", content: React.string("Week")}, {value: "day", content: React.string("Day")}, {value: "month", content: React.string("Month")})})} />}
      actions={content: [PrimaryAction({children: React.string("New booking")})]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[
            head(14),
            head(15),
            head(16),
          ]
          hours=[
            hour(9),
            hour(10),
            hour(11),
            hour(12),
            hour(13),
            hour(14),
          ]
          columns=[
            {
              key: "2025-04-14",
              children: <>
                <Calendar.Closure
                  name={React.string("Hall closed for floor cleaning")}
                  href="../../../record/"
                  props={{style: placement(4, 2)}}
                />
                <Calendar.Block
                  name={React.string("Community lunch")}
                  href="../../../record/"
                  span={span(14, "13:00", "14:00")}
                  kind="Booking"
                  props={{ariaLabel: "Community lunch, 13:00–14:00", style: placement(5, 1)}}
                />
              </>,
              props: {},
            },
            {
              key: "2025-04-15",
              children: <Calendar.Block
                name={React.string("Yoga class")}
                href="../../../record/"
                span={span(15, "10:00", "11:00")}
                kind="Booking"
                props={{ariaLabel: "Yoga class, 10:00–11:00", style: placement(2, 1)}}
              />,
              props: {},
            },
            {
              key: "2025-04-16",
              children: <>
                <Calendar.Closure
                  name={React.string("Meeting Room closed for rewiring")}
                  href="../../../record/"
                  props={{style: sized(2, 2, "calc(var(--kit-hour-height) * 1.5)")}}
                  nameProps={{draggable: true}}
                />
                <Calendar.Block
                  name={React.string("Parish council")}
                  href="../../../record/"
                  span={span(16, "11:00", "12:00")}
                  kind="Meeting"
                  props={{ariaLabel: "Parish council, 11:00–12:00", style: placement(3, 1)}}
                />
              </>,
              props: {},
            },
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day
            day={day(14)}
            items=[
              item(
                ~key="cl-131",
                ~name="Hall closed for floor cleaning",
                ~span=span(14, "12:00", "14:00"),
                ~kind="Closure",
              ),
              item(
                ~key="bk-6101",
                ~name="Community lunch",
                ~span=span(14, "13:00", "14:00"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(15)}
            items=[
              item(
                ~key="bk-6102",
                ~name="Yoga class",
                ~span=span(15, "10:00", "11:00"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(16)}
            items=[
              item(
                ~key="cl-132",
                ~name="Meeting Room closed for rewiring",
                ~span=span(16, "10:00", "11:30"),
                ~kind="Closure",
              ),
              item(
                ~key="bk-6103",
                ~name="Parish council",
                ~span=span(16, "11:00", "12:00"),
                ~kind="Meeting",
              ),
            ]
          />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, closure-under-block

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let hour = (hour): DeskGrid.hour => {time: KitReact.Time.clock(Specimen_Reader.reader, {hour, minute: 0}), props: {}}

let day = day => KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day})

let span = (on, start, end) =>
  KitReact.Time.interval(
    Specimen_Reader.reader,
    ~start=Date.fromString(`2025-04-${Int.toString(on)}T${start}:00+01:00`),
    ~end=Date.fromString(`2025-04-${Int.toString(on)}T${end}:00+01:00`),
  )

let head = (on): DeskGrid.head => {key: KitReact.Time.datetime(day(on)), column: DeskGrid.Day(day(on)), props: {}}

let placement = (row, span, at) =>
  ReactDOM.Style.unsafeAddProp(({gridRow: `${Int.toString(row)} / span ${Int.toString(span)}`}: ReactDOM.Style.t), "--kit-calendar-block-part", Int.toString(at))
let sized = (style, size) => ReactDOM.Style.unsafeAddProp(style, "--kit-calendar-block-size", size)
let offset = (style, inset) => ReactDOM.Style.unsafeAddProp(style, "--kit-calendar-block-offset", inset)
let parts = count => ReactDOM.Style.unsafeAddProp({}, "--kit-calendar-column-parts", Int.toString(count))

let item = (~key, ~name, ~span, ~kind): Calendar.itemRow => {
  key,
  name,
  href: Some("../../../record/"),
  span,
  kind,
  standings: [],
  standingWords: [],
  room: None,
  state: None,
  props: {},
}

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-closure-under-block"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-closure-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Calendar controls"}}
      range={
        days: KitReact.Time.dayRange(Specimen_Reader.reader, ~first={year: 2025, month: 4, day: 14}, ~last={year: 2025, month: 4, day: 18}),
        today: {label: "Today", props: {}},
        previous: {label: "Previous week", href: "?week=previous", props: {rel: "prev"}},
        next: {label: "Next week", href: "?week=next", props: {rel: "next"}},
        date: {group: {id: "calendar-closure-under-block-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: Specimen_Reader.reader, value: Day(KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14})), today: KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14}), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-closure-scope-form-group",
          kind: Select,
          label: React.string("Hall"),
          labelProps: {htmlFor: "calendar-closure-scope"},
          controlProps: {id: "calendar-closure-scope"},
          options: [
            {label: React.string("All halls"), value: "all", props: {}},
            {label: React.string("Oakmere Hall"), value: "oakmere", props: {}},
          ],
        }),
        FormGroup({id: "calendar-closure-room-form-group",
          kind: Select,
          label: React.string("Room"),
          labelProps: {htmlFor: "calendar-closure-room"},
          controlProps: {id: "calendar-closure-room"},
          options: [
            {label: React.string("All rooms"), value: "all", props: {}},
            {label: React.string("Main Hall"), value: "main-hall", props: {}},
            {label: React.string("Meeting Room"), value: "meeting-room", props: {}},
          ],
        }),
      ]
      view={<KitReact.Roles.FormGroup id="calendar-closure-under-block-view-form-group" label={React.string("View")} kind={KitReact.Roles.SegmentedControl.controlSet({name: "calendar-closure-under-block-view", value: "week", options: Three({value: "week", content: React.string("Week")}, {value: "day", content: React.string("Day")}, {value: "month", content: React.string("Month")})})} />}
      actions={content: [PrimaryAction({children: React.string("New booking")})]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[
            head(14),
            head(15),
            head(16),
            head(17),
            head(18),
          ]
          hours=[
            hour(13),
            hour(14),
            hour(15),
            hour(16),
            hour(17),
            hour(18),
          ]
          columns=[
            {
              key: "2025-04-14",
              children: <>
                <Calendar.Closure
                  name={React.string("Floor repair")}
                  href="../../../record/"
                  props={{style: placement(4, 1, 2)->offset("calc(var(--kit-hour-height) / 2)")->sized("calc(var(--kit-hour-height) / 2)")}}
                />
                <Calendar.Block
                  name={React.string("Teen jazz")}
                  href="../../../record/"
                  span={span(14, "16:00", "17:00")}
                  kind="Class"
                  props={{ariaLabel: "Teen jazz, 16:00–17:00", style: placement(4, 1, 1)}}
                />
              </>,
              props: {style: parts(2)},
            },
            {
              key: "2025-04-15",
              children: <>
                <Calendar.Closure
                  name={React.string("Fire drill")}
                  href="../../../record/"
                  props={{style: placement(2, 1, 2)->sized("calc(var(--kit-hour-height) / 4)")}}
                />
                <Calendar.Block
                  name={React.string("Junior ballet")}
                  href="../../../record/"
                  span={span(15, "14:00", "14:30")}
                  kind="Class"
                  props={{ariaLabel: "Junior ballet, 14:00–14:30", style: placement(2, 1, 1)->sized("calc(var(--kit-hour-height) / 2)")}}
                />
              </>,
              props: {style: parts(2)},
            },
            {
              key: "2025-04-16",
              children: <>
                <Calendar.Closure
                  name={React.string("Sound system check")}
                  href="../../../record/"
                  props={{style: placement(1, 2, 2)->offset("calc(var(--kit-hour-height) / 2)")->sized("var(--kit-hour-height)")}}
                />
                <Calendar.Block
                  name={React.string("Adult contemporary")}
                  href="../../../record/"
                  span={span(16, "13:00", "15:00")}
                  kind="Class"
                  props={{ariaLabel: "Adult contemporary, 13:00–15:00", style: placement(1, 2, 1)}}
                />
              </>,
              props: {style: parts(2)},
            },
            {
              key: "2025-04-17",
              children: <>
                <Calendar.Closure
                  name={React.string("Heating maintenance")}
                  href="../../../record/"
                  props={{style: placement(4, 2, 2)}}
                />
                <Calendar.Block
                  name={React.string("Ballroom")}
                  href="../../../record/"
                  span={span(17, "15:30", "16:30")}
                  kind="Class"
                  props={{ariaLabel: "Ballroom, 15:30–16:30", style: placement(3, 2, 1)->offset("calc(var(--kit-hour-height) / 2)")->sized("var(--kit-hour-height)")}}
                />
              </>,
              props: {style: parts(2)},
            },
            {
              key: "2025-04-18",
              children: <>
                <Calendar.Closure
                  name={React.string("Mirror cleaning")}
                  href="../../../record/"
                  props={{style: placement(2, 1, 3)->offset("calc(var(--kit-hour-height) / 2)")->sized("calc(var(--kit-hour-height) / 2)")}}
                />
                <Calendar.Block
                  name={React.string("Hip hop")}
                  href="../../../record/"
                  span={span(18, "14:00", "15:00")}
                  kind="Class"
                  props={{ariaLabel: "Hip hop, 14:00–15:00", style: placement(2, 1, 1)}}
                />
                <Calendar.Block
                  name={React.string("Tap")}
                  href="../../../record/"
                  span={span(18, "14:30", "15:30")}
                  kind="Class"
                  props={{ariaLabel: "Tap, 14:30–15:30", style: placement(2, 2, 2)->offset("calc(var(--kit-hour-height) / 2)")->sized("var(--kit-hour-height)")}}
                />
              </>,
              props: {style: parts(3)},
            },
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day
            day={day(14)}
            items=[
              item(~key="bk-7101", ~name="Teen jazz", ~span=span(14, "16:00", "17:00"), ~kind="Class"),
              item(~key="cl-171", ~name="Floor repair", ~span=span(14, "16:30", "17:00"), ~kind="Closure"),
            ]
          />
          <Calendar.Day
            day={day(15)}
            items=[
              item(~key="cl-172", ~name="Fire drill", ~span=span(15, "14:00", "14:15"), ~kind="Closure"),
              item(~key="bk-7102", ~name="Junior ballet", ~span=span(15, "14:00", "14:30"), ~kind="Class"),
            ]
          />
          <Calendar.Day
            day={day(16)}
            items=[
              item(~key="bk-7103", ~name="Adult contemporary", ~span=span(16, "13:00", "15:00"), ~kind="Class"),
              item(~key="cl-173", ~name="Sound system check", ~span=span(16, "13:30", "14:30"), ~kind="Closure"),
            ]
          />
          <Calendar.Day
            day={day(17)}
            items=[
              item(~key="bk-7104", ~name="Ballroom", ~span=span(17, "15:30", "16:30"), ~kind="Class"),
              item(~key="cl-174", ~name="Heating maintenance", ~span=span(17, "16:00", "18:00"), ~kind="Closure"),
            ]
          />
          <Calendar.Day
            day={day(18)}
            items=[
              item(~key="bk-7105", ~name="Hip hop", ~span=span(18, "14:00", "15:00"), ~kind="Class"),
              item(~key="cl-175", ~name="Mirror cleaning", ~span=span(18, "14:30", "15:00"), ~kind="Closure"),
              item(~key="bk-7106", ~name="Tap", ~span=span(18, "14:30", "15:30"), ~kind="Class"),
            ]
          />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, whole-day-closure

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let hour = (hour): DeskGrid.hour => {time: KitReact.Time.clock(Specimen_Reader.reader, {hour, minute: 0}), props: {}}

let day = day => KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day})

let span = (on, start, end) =>
  KitReact.Time.interval(
    Specimen_Reader.reader,
    ~start=Date.fromString(`2025-04-${Int.toString(on)}T${start}:00+01:00`),
    ~end=Date.fromString(`2025-04-${Int.toString(on)}T${end}:00+01:00`),
  )

let head = (on): DeskGrid.head => {key: KitReact.Time.datetime(day(on)), column: DeskGrid.Day(day(on)), props: {}}

let band = (name, first, last, stack): DeskGrid.band => {
  name: React.string(name),
  href: Some("../../../record/"),
  first,
  last,
  stack,
  props: {},
}

let placement = (row, span) => (
  {gridRow: `${Int.toString(row)} / span ${Int.toString(span)}`}: ReactDOM.Style.t
)
let sized = (row, span, size) =>
  ReactDOM.Style.unsafeAddProp(placement(row, span), "--kit-calendar-block-size", size)
let offsetSized = (row, span, offset, size) =>
  ReactDOM.Style.unsafeAddProp(sized(row, span, size), "--kit-calendar-block-offset", offset)

let hourHeight = "var(--kit-hour-height)"

let bandRow = (key, name): Calendar.bandRow => {
  key,
  name: React.string(name),
  href: Some("../../../record/"),
  state: None,
  props: {},
}

let item = (~key, ~name, ~span, ~kind, ~standings=[], ~standingWords=[]): Calendar.itemRow => {
  key,
  name,
  href: Some("../../../record/"),
  span,
  kind,
  standings,
  standingWords,
  room: None,
  state: None,
  props: {},
}

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-whole-day-closure"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Calendar controls"}}
      range={
        days: KitReact.Time.dayRange(Specimen_Reader.reader, ~first={year: 2025, month: 4, day: 14}, ~last={year: 2025, month: 4, day: 20}),
        today: {label: "Today", props: {}},
        previous: {label: "Previous week", href: "?week=previous", props: {rel: "prev"}},
        next: {label: "Next week", href: "?week=next", props: {rel: "next"}},
        date: {group: {id: "calendar-whole-day-closure-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: Specimen_Reader.reader, value: Day(KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14})), today: KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14}), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-scope-form-group",
          kind: Select,
          label: React.string("Hall"),
          labelProps: {htmlFor: "calendar-scope"},
          controlProps: {id: "calendar-scope"},
          options: [
            {label: React.string("All halls"), value: "all", props: {}},
            {label: React.string("Oakmere Hall"), value: "oakmere", props: {}},
          ],
        }),
        FormGroup({id: "calendar-scope-room-form-group",
          kind: Select,
          label: React.string("Room"),
          labelProps: {htmlFor: "calendar-scope-room"},
          controlProps: {id: "calendar-scope-room"},
          options: [
            {label: React.string("All rooms"), value: "all", props: {}},
            {label: React.string("Main Hall"), value: "main-hall", props: {}},
            {label: React.string("Meeting Room"), value: "meeting-room", props: {}},
          ],
        }),
      ]
      view={<KitReact.Roles.FormGroup id="calendar-whole-day-closure-view-form-group" label={React.string("View")} kind={KitReact.Roles.SegmentedControl.controlSet({name: "calendar-whole-day-closure-view", value: "week", options: Two({value: "week", content: React.string("Week")}, {value: "day", content: React.string("Day")})})} />}
      actions={content: [PrimaryAction({children: React.string("New booking")})]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[
            head(14),
            head(15),
            head(16),
            head(17),
            head(18),
            head(19),
            head(20),
          ]
          bands=[
            band("Hall closed for rewiring", 4, 4, 1),
            band("Hall closed for resurfacing", 6, 6, 1),
          ]
          hours=[
            hour(8),
            hour(9),
            hour(10),
            hour(11),
            hour(12),
            hour(13),
            hour(14),
            hour(15),
            hour(16),
            hour(17),
          ]
          columns=[
            {
              key: "2025-04-14",
              children: <Calendar.Block
                name={React.string("Board meeting")}
                href="../../../record/"
                span={span(14, "09:00", "10:00")}
                kind="Meeting"
                props={{ariaLabel: "Board meeting, 09:00–10:00", style: placement(2, 1)}}
              />,
              props: {},
            },
            {
              key: "2025-04-15",
              children: <Calendar.Block
                name={React.string("Yoga class")}
                href="../../../record/"
                span={span(15, "10:00", "11:00")}
                kind="Booking"
                props={{ariaLabel: "Yoga class, 10:00–11:00", style: placement(3, 1)}}
              />,
              props: {},
            },
            {
              key: "2025-04-16",
              children: <Calendar.Block
                name={React.string("Parish council")}
                href="../../../record/"
                span={span(16, "11:00", "12:00")}
                kind="Meeting"
                props={{ariaLabel: "Parish council, 11:00–12:00", style: placement(4, 1)}}
              />,
              props: {},
            },
            {
              key: "2025-04-17",
              children: <>
                <Calendar.Block
                  name={React.string("Toddler group")}
                  href="../../../record/"
                  span={span(17, "09:30", "11:00")}
                  kind="Booking"
                  standings=[Cancelled]
                  standingWords=["Cancelled"]
                  props={{ariaLabel: "Toddler group, 09:30–11:00, Cancelled",
                    style: offsetSized(
                      2,
                      2,
                      `calc(${hourHeight} / 2)`,
                      `calc(${hourHeight} * 1.5)`,
                    ),
                  }}
                />
                <Calendar.Block
                  name={React.string("Choir rehearsal")}
                  href="../../../record/"
                  span={span(17, "14:00", "15:30")}
                  kind="Booking"
                  standings=[Cancelled]
                  standingWords=["Cancelled"]
                  props={{ariaLabel: "Choir rehearsal, 14:00–15:30, Cancelled", style: sized(7, 2, `calc(${hourHeight} * 1.5)`)}}
                />
              </>,
              props: {},
            },
            {
              key: "2025-04-18",
              children: <Calendar.Block
                name={React.string("Wedding rehearsal")}
                href="../../../record/"
                span={span(18, "12:00", "13:00")}
                kind="Booking"
                props={{ariaLabel: "Wedding rehearsal, 12:00–13:00", style: placement(5, 1)}}
              />,
              props: {},
            },
            {
              key: "2025-04-19",
              children: <Calendar.Block
                name={React.string("Yoga class")}
                href="../../../record/"
                span={span(19, "10:00", "11:00")}
                kind="Booking"
                standings=[Cancelled]
                standingWords=["Cancelled"]
                props={{ariaLabel: "Yoga class, 10:00–11:00, Cancelled", style: placement(3, 1)}}
              />,
              props: {},
            },
            {
              key: "2025-04-20",
              children: <Calendar.Block
                name={React.string("Sunday service")}
                href="../../../record/"
                span={span(20, "10:00", "11:30")}
                kind="Booking"
                props={{ariaLabel: "Sunday service, 10:00–11:30", style: sized(3, 2, `calc(${hourHeight} * 1.5)`)}}
              />,
              props: {},
            },
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day
            day={day(14)}
            items=[
              item(
                ~key="bk-6001",
                ~name="Board meeting",
                ~span=span(14, "09:00", "10:00"),
                ~kind="Meeting",
              ),
            ]
          />
          <Calendar.Day
            day={day(15)}
            items=[
              item(
                ~key="bk-6002",
                ~name="Yoga class",
                ~span=span(15, "10:00", "11:00"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(16)}
            items=[
              item(
                ~key="bk-6003",
                ~name="Parish council",
                ~span=span(16, "11:00", "12:00"),
                ~kind="Meeting",
              ),
            ]
          />
          <Calendar.Day
            day={day(17)}
            bands=[bandRow("cl-124", "Hall closed for rewiring")]
            items=[
              item(
                ~key="bk-6004",
                ~name="Toddler group",
                ~span=span(17, "09:30", "11:00"),
                ~kind="Booking",
                ~standings=[Cancelled],
                ~standingWords=["Cancelled"],
              ),
              item(
                ~key="bk-6005",
                ~name="Choir rehearsal",
                ~span=span(17, "14:00", "15:30"),
                ~kind="Booking",
                ~standings=[Cancelled],
                ~standingWords=["Cancelled"],
              ),
            ]
          />
          <Calendar.Day
            day={day(18)}
            items=[
              item(
                ~key="bk-6006",
                ~name="Wedding rehearsal",
                ~span=span(18, "12:00", "13:00"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(19)}
            bands=[bandRow("cl-125", "Hall closed for resurfacing")]
            items=[
              item(
                ~key="bk-6007",
                ~name="Yoga class",
                ~span=span(19, "10:00", "11:00"),
                ~kind="Booking",
                ~standings=[Cancelled],
                ~standingWords=["Cancelled"],
              ),
            ]
          />
          <Calendar.Day
            day={day(20)}
            items=[
              item(
                ~key="bk-6008",
                ~name="Sunday service",
                ~span=span(20, "10:00", "11:30"),
                ~kind="Booking",
              ),
            ]
          />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, overlap

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let hour = (hour): DeskGrid.hour => {time: KitReact.Time.clock(Specimen_Reader.reader, {hour, minute: 0}), props: {}}

let day = day => KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day})

let span = (on, start, end) =>
  KitReact.Time.interval(
    Specimen_Reader.reader,
    ~start=Date.fromString(`2025-04-${Int.toString(on)}T${start}:00+01:00`),
    ~end=Date.fromString(`2025-04-${Int.toString(on)}T${end}:00+01:00`),
  )

let head = (on): DeskGrid.head => {key: KitReact.Time.datetime(day(on)), column: DeskGrid.Day(day(on)), props: {}}

let placement = (row, span) => (
  {gridRow: `${Int.toString(row)} / span ${Int.toString(span)}`}: ReactDOM.Style.t
)
let part = (row, span, at) =>
  ReactDOM.Style.unsafeAddProp(placement(row, span), "--kit-calendar-block-part", Int.toString(at))
let parts = count =>
  ReactDOM.Style.unsafeAddProp({}, "--kit-calendar-column-parts", Int.toString(count))

let hourHeight = "var(--kit-hour-height)"

let band = (name, stack): DeskGrid.band => {
  name: React.string(name),
  href: Some("../../../record/"),
  first: 1,
  last: 1,
  stack,
  props: {},
}

let bandRow = (key, name): Calendar.bandRow => {
  key,
  name: React.string(name),
  href: Some("../../../record/"),
  state: None,
  props: {},
}

let item = (~key, ~name, ~span, ~kind, ~room=?): Calendar.itemRow => {
  key,
  name,
  href: Some("../../../record/"),
  span,
  kind,
  standings: [],
  standingWords: [],
  room,
  state: None,
  props: {},
}

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-overlap"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-overlap-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Calendar controls"}}
      range={
        days: KitReact.Time.dayRange(Specimen_Reader.reader, ~first={year: 2025, month: 4, day: 14}, ~last={year: 2025, month: 4, day: 16}),
        today: {label: "Today", props: {}},
        previous: {label: "Previous week", href: "?week=previous", props: {rel: "prev"}},
        next: {label: "Next week", href: "?week=next", props: {rel: "next"}},
        date: {group: {id: "calendar-overlap-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: Specimen_Reader.reader, value: Day(KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14})), today: KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14}), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-overlap-scope-form-group",
          kind: Select,
          label: React.string("Hall"),
          labelProps: {htmlFor: "calendar-overlap-scope"},
          controlProps: {id: "calendar-overlap-scope"},
          options: [
            {label: React.string("All halls"), value: "all", props: {}},
            {label: React.string("Oakmere Hall"), value: "oakmere", props: {}},
          ],
        }),
        FormGroup({id: "calendar-overlap-room-form-group",
          kind: Select,
          label: React.string("Room"),
          labelProps: {htmlFor: "calendar-overlap-room"},
          controlProps: {id: "calendar-overlap-room"},
          options: [
            {label: React.string("All rooms"), value: "all", props: {}},
            {label: React.string("Main Hall"), value: "main-hall", props: {}},
          ],
        }),
      ]
      view={<KitReact.Roles.FormGroup id="calendar-overlap-view-form-group" label={React.string("View")} kind={KitReact.Roles.SegmentedControl.controlSet({name: "calendar-overlap-view", value: "week", options: Three({value: "week", content: React.string("Week")}, {value: "day", content: React.string("Day")}, {value: "month", content: React.string("Month")})})} />}
      actions={content: [PrimaryAction({children: React.string("New booking")})]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[
            head(14),
            head(15),
            head(16),
          ]
          bands=[band("Open day", 1), band("Meeting Room decorated", 2)]
          hours=[
            hour(9),
            hour(10),
            hour(11),
            hour(12),
            hour(13),
            hour(14),
          ]
          columns=[
            {
              key: "2025-04-14",
              children: <>
                <Calendar.Block
                  name={React.string("Yoga class")}
                  href="../../../record/"
                  span={span(14, "10:00", "11:00")}
                  kind="Booking"
                  props={{ariaLabel: "Yoga class, 10:00–11:00", style: placement(2, 1)}}
                />
                <Calendar.Folded
                  name={React.string("3 items")}
                  href="../day-rooms/"
                  props={{ariaLabel: "3 items, 11:00–12:00", style: placement(3, 1)}}
                />
                <Calendar.Block
                  name={React.string("Wedding reception")}
                  href="../../../record/"
                  span={span(14, "12:00", "15:00")}
                  kind="Booking"
                  props={{ariaLabel: "Wedding reception, 12:00–15:00", style: placement(4, 3)}}
                />
              </>,
              props: {},
            },
            {
              key: "2025-04-15",
              children: <Calendar.Block
                name={React.string("Yoga class")}
                href="../../../record/"
                span={span(15, "10:00", "11:00")}
                kind="Booking"
                props={{ariaLabel: "Yoga class, 10:00–11:00", style: placement(2, 1)}}
              />,
              props: {},
            },
            {
              key: "2025-04-16",
              children: <>
                <Calendar.Block
                  name={React.string("Toddler group")}
                  href="../../../record/"
                  span={span(16, "09:00", "11:00")}
                  kind="Booking"
                  props={{ariaLabel: "Toddler group, 09:00–11:00", style: part(1, 2, 1)}}
                />
                <Calendar.Block
                  name={React.string("Fire inspection")}
                  href="../../../record/"
                  span={span(16, "09:30", "10:30")}
                  kind="Inspection"
                  props={{ariaLabel: "Fire inspection, 09:30–10:30",
                    style: ReactDOM.Style.unsafeAddProp(
                      ReactDOM.Style.unsafeAddProp(
                        part(1, 2, 2),
                        "--kit-calendar-block-offset",
                        `calc(${hourHeight} / 2)`,
                      ),
                      "--kit-calendar-block-size",
                      hourHeight,
                    ),
                  }}
                />
              </>,
              props: {style: parts(2)},
            },
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day
            day={day(14)}
            bands=[bandRow("ev-207", "Open day"), bandRow("wk-311", "Meeting Room decorated")]
            items=[
              item(
                ~key="bk-6201",
                ~name="Yoga class",
                ~span=span(14, "10:00", "11:00"),
                ~kind="Booking",
                ~room="Main Hall",
              ),
              item(
                ~key="bk-6202",
                ~name="Hire request",
                ~span=span(14, "11:00", "12:00"),
                ~kind="Requested",
              ),
              item(
                ~key="bk-6203",
                ~name="Parish council",
                ~span=span(14, "11:00", "12:00"),
                ~kind="Meeting",
                ~room="Meeting Room",
              ),
              item(
                ~key="bk-6204",
                ~name="Community lunch",
                ~span=span(14, "11:30", "12:00"),
                ~kind="Booking",
                ~room="Main Hall",
              ),
              item(
                ~key="bk-6205",
                ~name="Wedding reception",
                ~span=span(14, "12:00", "15:00"),
                ~kind="Booking",
                ~room="Main Hall",
              ),
            ]
          />
          <Calendar.Day
            day={day(15)}
            items=[
              item(
                ~key="bk-6206",
                ~name="Yoga class",
                ~span=span(15, "10:00", "11:00"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(16)}
            items=[
              item(
                ~key="bk-6207",
                ~name="Toddler group",
                ~span=span(16, "09:00", "11:00"),
                ~kind="Booking",
              ),
              item(
                ~key="bk-6208",
                ~name="Fire inspection",
                ~span=span(16, "09:30", "10:30"),
                ~kind="Inspection",
              ),
            ]
          />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, contiguous-run

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let hour = (hour): DeskGrid.hour => {time: KitReact.Time.clock(Specimen_Reader.reader, {hour, minute: 0}), props: {}}

let day = day => KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day})

let span = (on, start, end) =>
  KitReact.Time.interval(
    Specimen_Reader.reader,
    ~start=Date.fromString(`2025-04-${Int.toString(on)}T${start}:00+01:00`),
    ~end=Date.fromString(`2025-04-${Int.toString(on)}T${end}:00+01:00`),
  )

let head = (on): DeskGrid.head => {key: KitReact.Time.datetime(day(on)), column: DeskGrid.Day(day(on)), props: {}}

let row = (row): ReactDOM.Style.t => {gridRow: `${Int.toString(row)} / span 1`}

let placement = (line, size) =>
  ReactDOM.Style.unsafeAddProp(row(line), "--kit-calendar-block-size", size)

let offsetPlacement = (line, offset, size) =>
  ReactDOM.Style.unsafeAddProp(placement(line, size), "--kit-calendar-block-offset", offset)

let hourHeight = "var(--kit-hour-height)"

let nothing = <p> {React.string("Nothing booked")} </p>

let column = (key): DeskGrid.column => {key, children: React.null, props: {}}

let item = (~key, ~name, ~span, ~kind, ~room): Calendar.itemRow => {
  key,
  name,
  href: Some("../../../record/"),
  span,
  kind,
  standings: [],
  standingWords: [],
  room: Some(room),
  state: None,
  props: {},
}

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-contiguous-run"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-contiguous-run-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Calendar controls"}}
      range={
        days: KitReact.Time.dayRange(Specimen_Reader.reader, ~first={year: 2025, month: 4, day: 14}, ~last={year: 2025, month: 4, day: 20}),
        today: {label: "Today", props: {}},
        previous: {label: "Previous week", href: "?week=previous", props: {rel: "prev"}},
        next: {label: "Next week", href: "?week=next", props: {rel: "next"}},
        date: {group: {id: "calendar-contiguous-run-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: Specimen_Reader.reader, value: Day(KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14})), today: KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14}), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-contiguous-run-scope-form-group",
          kind: Select,
          label: React.string("Hall"),
          labelProps: {htmlFor: "calendar-contiguous-run-scope"},
          controlProps: {id: "calendar-contiguous-run-scope"},
          options: [
            {label: React.string("All halls"), value: "all", props: {}},
            {label: React.string("Oakmere Hall"), value: "oakmere", props: {}},
          ],
        }),
        FormGroup({id: "calendar-contiguous-run-room-form-group",
          kind: Select,
          label: React.string("Room"),
          labelProps: {htmlFor: "calendar-contiguous-run-room"},
          controlProps: {id: "calendar-contiguous-run-room"},
          options: [
            {label: React.string("All rooms"), value: "all", props: {}},
            {label: React.string("Main Hall"), value: "main-hall", props: {}},
            {label: React.string("Meeting Room"), value: "meeting-room", props: {}},
          ],
        }),
      ]
      view={<KitReact.Roles.FormGroup id="calendar-contiguous-run-view-form-group" label={React.string("View")} kind={KitReact.Roles.SegmentedControl.controlSet({name: "calendar-contiguous-run-view", value: "week", options: Three({value: "week", content: React.string("Week")}, {value: "day", content: React.string("Day")}, {value: "month", content: React.string("Month")})})} />}
      actions={content: [PrimaryAction({children: React.string("New booking")})]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[
            head(14),
            head(15),
            head(16),
            head(17),
            head(18),
            head(19),
            head(20),
          ]
          hours=[
            hour(9),
            hour(10),
            hour(11),
            hour(12),
          ]
          columns=[
            {
              key: "2025-04-14",
              children: <>
                <Calendar.FoldedRun
                  span={span(14, "09:00", "09:20")}
                  count={[KitReact.Words.Text("2 bookings")]}
                  href="../day-one-column/"
                  props={{style: placement(1, "calc(var(--kit-hour-height) / 3)")}}
                />
                <Calendar.FoldedRun
                  span={span(14, "10:00", "10:35")}
                  count={[KitReact.Words.Text("3 room inspections")]}
                  href="../day-one-column/"
                  props={{style: placement(2, "calc(var(--kit-hour-height) * 7 / 12)")}}
                />
              </>,
              props: {},
            },
            {
              key: "2025-04-15",
              children: <>
                <Calendar.Block
                  name={React.string("Yoga")}
                  href="../../../record/"
                  span={span(15, "09:00", "09:15")}
                  kind="Booking"
                  props={{ariaLabel: "Yoga, 09:00–09:15", style: placement(1, `calc(${hourHeight} / 4)`)}}
                />
                <Calendar.Block
                  name={React.string("Singing")}
                  href="../../../record/"
                  span={span(15, "09:45", "10:00")}
                  kind="Booking"
                  props={{ariaLabel: "Singing, 09:45–10:00",
                    style: offsetPlacement(1, `calc(${hourHeight} * 3 / 4)`, `calc(${hourHeight} / 4)`),
                  }}
                />
              </>,
              props: {},
            },
            {
              key: "2025-04-16",
              children: <Calendar.FoldedRun
                span={span(16, "09:00", "09:45")}
                count={[KitReact.Words.Text("3 bookings")]}
                href="../day-one-column/"
                props={{style: placement(1, `calc(${hourHeight} * 3 / 4)`)}}
              />,
              props: {},
            },
            {
              key: "2025-04-17",
              children: <Calendar.FoldedRun
                span={span(17, "09:00", "10:00")}
                count={[KitReact.Words.Text("4 inspections")]}
                href="../day-one-column/"
                props={{style: row(1)}}
              />,
              props: {},
            },
            {
              key: "2025-04-18",
              children: <Calendar.FoldedRun
                span={span(18, "09:00", "09:30")}
                count={[KitReact.Words.Text("2 inspections")]}
                href="../day-one-column/"
                props={{style: placement(1, `calc(${hourHeight} / 2)`)}}
              />,
              props: {},
            },
            column("2025-04-19"),
            column("2025-04-20"),
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day
            day={day(14)}
            items=[
              item(
                ~key="bk-7101",
                ~name="Chair stack",
                ~span=span(14, "09:00", "09:10"),
                ~kind="Booking",
                ~room="Main Hall",
              ),
              item(
                ~key="bk-7102",
                ~name="Trolley store",
                ~span=span(14, "09:10", "09:20"),
                ~kind="Booking",
                ~room="Main Hall",
              ),
              item(
                ~key="bk-7103",
                ~name="Room check",
                ~span=span(14, "10:00", "10:10"),
                ~kind="Inspection",
                ~room="Meeting Room",
              ),
              item(
                ~key="bk-7104",
                ~name="Fire door check",
                ~span=span(14, "10:10", "10:20"),
                ~kind="Inspection",
                ~room="Meeting Room",
              ),
              item(
                ~key="bk-7105",
                ~name="Meter reading",
                ~span=span(14, "10:20", "10:35"),
                ~kind="Inspection",
                ~room="Meeting Room",
              ),
            ]
          />
          <Calendar.Day
            day={day(15)}
            items=[
              item(
                ~key="bk-7201",
                ~name="Yoga",
                ~span=span(15, "09:00", "09:15"),
                ~kind="Booking",
                ~room="Main Hall",
              ),
              item(
                ~key="bk-7203",
                ~name="Singing",
                ~span=span(15, "09:45", "10:00"),
                ~kind="Booking",
                ~room="Main Hall",
              ),
            ]
          />
          <Calendar.Day
            day={day(16)}
            items=[
              item(
                ~key="bk-7301",
                ~name="Yoga",
                ~span=span(16, "09:00", "09:15"),
                ~kind="Booking",
                ~room="Main Hall",
              ),
              item(
                ~key="bk-7302",
                ~name="Judo",
                ~span=span(16, "09:15", "09:30"),
                ~kind="Booking",
                ~room="Main Hall",
              ),
              item(
                ~key="bk-7303",
                ~name="Singing",
                ~span=span(16, "09:30", "09:45"),
                ~kind="Booking",
                ~room="Main Hall",
              ),
            ]
          />
          <Calendar.Day
            day={day(17)}
            items=[
              item(
                ~key="bk-7401",
                ~name="Ramp check",
                ~span=span(17, "09:00", "09:15"),
                ~kind="Inspection",
                ~room="Meeting Room",
              ),
              item(
                ~key="bk-7402",
                ~name="Door widths",
                ~span=span(17, "09:15", "09:30"),
                ~kind="Inspection",
                ~room="Meeting Room",
              ),
              item(
                ~key="bk-7403",
                ~name="Hearing loop test",
                ~span=span(17, "09:30", "09:45"),
                ~kind="Inspection",
                ~room="Meeting Room",
              ),
              item(
                ~key="bk-7404",
                ~name="Signage check",
                ~span=span(17, "09:45", "10:00"),
                ~kind="Inspection",
                ~room="Meeting Room",
              ),
            ]
          />
          <Calendar.Day
            day={day(18)}
            items=[
              item(
                ~key="bk-7501",
                ~name="Gate check",
                ~span=span(18, "09:00", "09:10"),
                ~kind="Inspection",
                ~room="Meeting Room",
              ),
              item(
                ~key="bk-7502",
                ~name="Lift check",
                ~span=span(18, "09:20", "09:30"),
                ~kind="Inspection",
                ~room="Meeting Room",
              ),
            ]
          />
          <Calendar.Day day={day(19)} empty=nothing />
          <Calendar.Day day={day(20)} empty=nothing />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, cancelled

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let reader = Specimen_Reader.reader

let day = (day): KitReact.Time.t => KitReact.Time.day(reader, {year: 2025, month: 4, day})

let span = (start, finish) =>
  KitReact.Time.interval(reader, ~start=Date.fromString(start), ~end=Date.fromString(finish))

let hour = (hour): DeskGrid.hour => {time: KitReact.Time.clock(reader, {hour, minute: 0}), props: {}}

let head = (key, on): DeskGrid.head => {key, column: Day(day(on)), props: {}}

let placement = (row, span) => (
  {gridRow: `${Int.toString(row)} / span ${Int.toString(span)}`}: ReactDOM.Style.t
)
let sized = (row, span, size) =>
  ReactDOM.Style.unsafeAddProp(placement(row, span), "--kit-calendar-block-size", size)
let offsetSized = (row, span, offset, size) =>
  ReactDOM.Style.unsafeAddProp(sized(row, span, size), "--kit-calendar-block-offset", offset)

let hourHeight = "var(--kit-hour-height)"

let item = (~key, ~name, ~span, ~kind, ~standings=[], ~standingWords=[]): Calendar.itemRow => {
  key,
  name,
  href: Some("../../../record/"),
  span,
  kind,
  standings,
  standingWords,
  room: None,
  state: None,
  props: {},
}

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-cancelled"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-cancelled-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Calendar controls"}}
      range={
        days: KitReact.Time.dayRange(reader, ~first={year: 2025, month: 4, day: 14}, ~last={year: 2025, month: 4, day: 16}),
        today: {label: "Today", props: {}},
        previous: {label: "Previous week", href: "?week=previous", props: {rel: "prev"}},
        next: {label: "Next week", href: "?week=next", props: {rel: "next"}},
        date: {group: {id: "calendar-cancelled-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: Specimen_Reader.reader, value: Day(KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14})), today: KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14}), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-cancelled-scope-form-group",
          kind: Select,
          label: React.string("Hall"),
          labelProps: {htmlFor: "calendar-cancelled-scope"},
          controlProps: {id: "calendar-cancelled-scope"},
          options: [
            {label: React.string("All halls"), value: "all", props: {}},
            {label: React.string("Oakmere Hall"), value: "oakmere", props: {}},
          ],
        }),
        FormGroup({id: "calendar-cancelled-room-form-group",
          kind: Select,
          label: React.string("Room"),
          labelProps: {htmlFor: "calendar-cancelled-room"},
          controlProps: {id: "calendar-cancelled-room"},
          options: [
            {label: React.string("All rooms"), value: "all", props: {}},
            {label: React.string("Main Hall"), value: "main-hall", props: {}},
          ],
        }),
      ]
      view={<KitReact.Roles.FormGroup id="calendar-cancelled-view-form-group" label={React.string("View")} kind={KitReact.Roles.SegmentedControl.controlSet({name: "calendar-cancelled-view", value: "week", options: Three({value: "week", content: React.string("Week")}, {value: "day", content: React.string("Day")}, {value: "month", content: React.string("Month")})})} />}
      actions={content: [PrimaryAction({children: React.string("New booking")})]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[
            head("2025-04-14", 14),
            head("2025-04-15", 15),
            head("2025-04-16", 16),
          ]
          hours=[
            hour(9),
            hour(10),
            hour(11),
            hour(12),
            hour(13),
          ]
          columns=[
            {
              key: "2025-04-14",
              children: <Calendar.Block
                name={React.string("Toddler group")}
                href="../../../record/"
                span={span("2025-04-14T08:30:00Z", "2025-04-14T10:00:00Z")}
                kind="Booking"
                standings=[Cancelled]
                standingWords=["Cancelled"]
                props={{ariaLabel: "Toddler group, 09:30–11:00, Cancelled",
                  style: offsetSized(1, 2, `calc(${hourHeight} / 2)`, `calc(${hourHeight} * 1.5)`),
                }}
              />,
              props: {},
            },
            {
              key: "2025-04-15",
              children: <Calendar.Block
                name={React.string("Yoga class")}
                href="../../../record/"
                span={span("2025-04-15T09:00:00Z", "2025-04-15T10:00:00Z")}
                kind="Booking"
                props={{ariaLabel: "Yoga class, 10:00–11:00", style: placement(2, 1)}}
              />,
              props: {},
            },
            {
              key: "2025-04-16",
              children: <Calendar.Block
                name={React.string("Choir rehearsal")}
                href="../../../record/"
                span={span("2025-04-16T11:00:00Z", "2025-04-16T12:00:00Z")}
                kind="Booking"
                standings=[Cancelled]
                standingWords=["Cancelled"]
                props={{ariaLabel: "Choir rehearsal, 12:00–13:00, Cancelled", style: placement(4, 1)}}
              />,
              props: {},
            },
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day
            day={day(14)}
            items=[
              item(
                ~key="bk-5101",
                ~name="Toddler group",
                ~span=span("2025-04-14T08:30:00Z", "2025-04-14T10:00:00Z"),
                ~kind="Booking",
                ~standings=[Cancelled],
                ~standingWords=["Cancelled"],
              ),
            ]
          />
          <Calendar.Day
            day={day(15)}
            items=[
              item(
                ~key="bk-5102",
                ~name="Yoga class",
                ~span=span("2025-04-15T09:00:00Z", "2025-04-15T10:00:00Z"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(16)}
            items=[
              item(
                ~key="bk-5103",
                ~name="Choir rehearsal",
                ~span=span("2025-04-16T11:00:00Z", "2025-04-16T12:00:00Z"),
                ~kind="Booking",
                ~standings=[Cancelled],
                ~standingWords=["Cancelled"],
              ),
            ]
          />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, uncovered

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let reader = Specimen_Reader.reader

let day = (day): KitReact.Time.t => KitReact.Time.day(reader, {year: 2025, month: 4, day})

let span = (start, finish) =>
  KitReact.Time.interval(reader, ~start=Date.fromString(start), ~end=Date.fromString(finish))

let hour = (hour): DeskGrid.hour => {time: KitReact.Time.clock(reader, {hour, minute: 0}), props: {}}

let head = (key, on): DeskGrid.head => {key, column: Day(day(on)), props: {}}

let placement = (row, span) => (
  {gridRow: `${Int.toString(row)} / span ${Int.toString(span)}`}: ReactDOM.Style.t
)
let part = (row, span, at) =>
  ReactDOM.Style.unsafeAddProp(placement(row, span), "--kit-calendar-block-part", Int.toString(at))
let parts = count =>
  ReactDOM.Style.unsafeAddProp({}, "--kit-calendar-column-parts", Int.toString(count))

let hourHeight = "var(--kit-hour-height)"

let item = (~key, ~name, ~span, ~kind, ~standings=[], ~standingWords=[]): Calendar.itemRow => {
  key,
  name,
  href: Some("../../../record/"),
  span,
  kind,
  standings,
  standingWords,
  room: None,
  state: None,
  props: {},
}

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-uncovered"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-uncovered-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Calendar controls"}}
      range={
        days: KitReact.Time.dayRange(reader, ~first={year: 2025, month: 4, day: 14}, ~last={year: 2025, month: 4, day: 16}),
        today: {label: "Today", props: {}},
        previous: {label: "Previous week", href: "?week=previous", props: {rel: "prev"}},
        next: {label: "Next week", href: "?week=next", props: {rel: "next"}},
        date: {group: {id: "calendar-uncovered-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: Specimen_Reader.reader, value: Day(KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14})), today: KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14}), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-uncovered-scope-form-group",
          kind: Select,
          label: React.string("Hall"),
          labelProps: {htmlFor: "calendar-uncovered-scope"},
          controlProps: {id: "calendar-uncovered-scope"},
          options: [
            {label: React.string("All halls"), value: "all", props: {}},
            {label: React.string("Oakmere Hall"), value: "oakmere", props: {}},
          ],
        }),
        FormGroup({id: "calendar-uncovered-room-form-group",
          kind: Select,
          label: React.string("Room"),
          labelProps: {htmlFor: "calendar-uncovered-room"},
          controlProps: {id: "calendar-uncovered-room"},
          options: [
            {label: React.string("All rooms"), value: "all", props: {}},
            {label: React.string("Main Hall"), value: "main-hall", props: {}},
          ],
        }),
      ]
      view={<KitReact.Roles.FormGroup id="calendar-uncovered-view-form-group" label={React.string("View")} kind={KitReact.Roles.SegmentedControl.controlSet({name: "calendar-uncovered-view", value: "week", options: Three({value: "week", content: React.string("Week")}, {value: "day", content: React.string("Day")}, {value: "month", content: React.string("Month")})})} />}
      actions={content: [PrimaryAction({children: React.string("New booking")})]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[
            head("2025-04-14", 14),
            head("2025-04-15", 15),
            head("2025-04-16", 16),
          ]
          hours=[
            hour(9),
            hour(10),
            hour(11),
            hour(12),
            hour(13),
          ]
          columns=[
            {
              key: "2025-04-14",
              children: <>
                <Calendar.Block
                  name={React.string("Toddler group")}
                  href="../../../record/"
                  span={span("2025-04-14T08:00:00Z", "2025-04-14T09:00:00Z")}
                  kind="Booking"
                  standings=[Uncovered]
                  standingWords=["Cover needed"]
                  props={{ariaLabel: "Toddler group, 09:00–10:00, Cover needed", style: part(1, 1, 1)}}
                />
                <Calendar.Block
                  name={React.string("Story time")}
                  href="../../../record/"
                  span={span("2025-04-14T08:30:00Z", "2025-04-14T09:00:00Z")}
                  kind="Booking"
                  standings=[Uncovered]
                  standingWords=["Cover needed"]
                  props={{ariaLabel: "Story time, 09:30–10:00, Cover needed",
                    style: ReactDOM.Style.unsafeAddProp(
                      ReactDOM.Style.unsafeAddProp(
                        part(1, 1, 2),
                        "--kit-calendar-block-offset",
                        `calc(${hourHeight} / 2)`,
                      ),
                      "--kit-calendar-block-size",
                      `calc(${hourHeight} / 2)`,
                    ),
                  }}
                />
              </>,
              props: {style: parts(2)},
            },
            {
              key: "2025-04-15",
              children: <Calendar.Block
                name={React.string("Yoga class")}
                href="../../../record/"
                span={span("2025-04-15T09:00:00Z", "2025-04-15T10:00:00Z")}
                kind="Booking"
                props={{ariaLabel: "Yoga class, 10:00–11:00", style: placement(2, 1)}}
              />,
              props: {},
            },
            {
              key: "2025-04-16",
              children: <Calendar.Block
                name={React.string("Community lunch")}
                href="../../../record/"
                span={span("2025-04-16T11:00:00Z", "2025-04-16T12:00:00Z")}
                kind="Booking"
                standings=[Uncovered]
                standingWords=["Cover needed"]
                props={{ariaLabel: "Community lunch, 12:00–13:00, Cover needed", style: placement(4, 1)}}
              />,
              props: {},
            },
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day
            day={day(14)}
            items=[
              item(
                ~key="bk-5301",
                ~name="Toddler group",
                ~span=span("2025-04-14T08:00:00Z", "2025-04-14T09:00:00Z"),
                ~kind="Booking",
                ~standings=[Uncovered],
                ~standingWords=["Cover needed"],
              ),
              item(
                ~key="bk-5304",
                ~name="Story time",
                ~span=span("2025-04-14T08:30:00Z", "2025-04-14T09:00:00Z"),
                ~kind="Booking",
                ~standings=[Uncovered],
                ~standingWords=["Cover needed"],
              ),
            ]
          />
          <Calendar.Day
            day={day(15)}
            items=[
              item(
                ~key="bk-5302",
                ~name="Yoga class",
                ~span=span("2025-04-15T09:00:00Z", "2025-04-15T10:00:00Z"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(16)}
            items=[
              item(
                ~key="bk-5303",
                ~name="Community lunch",
                ~span=span("2025-04-16T11:00:00Z", "2025-04-16T12:00:00Z"),
                ~kind="Booking",
                ~standings=[Uncovered],
                ~standingWords=["Cover needed"],
              ),
            ]
          />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, clashing

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let reader = Specimen_Reader.reader

let day = (day): KitReact.Time.t => KitReact.Time.day(reader, {year: 2025, month: 4, day})

let span = (start, finish) =>
  KitReact.Time.interval(reader, ~start=Date.fromString(start), ~end=Date.fromString(finish))

let hour = (hour): DeskGrid.hour => {time: KitReact.Time.clock(reader, {hour, minute: 0}), props: {}}

let head = (key, on): DeskGrid.head => {key, column: Day(day(on)), props: {}}

let placement = (row, span) => (
  {gridRow: `${Int.toString(row)} / span ${Int.toString(span)}`}: ReactDOM.Style.t
)
let part = (row, span, at) =>
  ReactDOM.Style.unsafeAddProp(placement(row, span), "--kit-calendar-block-part", Int.toString(at))
let parts = count =>
  ReactDOM.Style.unsafeAddProp({}, "--kit-calendar-column-parts", Int.toString(count))

let hourHeight = "var(--kit-hour-height)"

let item = (~key, ~name, ~span, ~kind, ~standings=[], ~standingWords=[]): Calendar.itemRow => {
  key,
  name,
  href: Some("../../../record/"),
  span,
  kind,
  standings,
  standingWords,
  room: None,
  state: None,
  props: {},
}

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-clashing"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-clashing-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Calendar controls"}}
      range={
        days: KitReact.Time.dayRange(reader, ~first={year: 2025, month: 4, day: 14}, ~last={year: 2025, month: 4, day: 16}),
        today: {label: "Today", props: {}},
        previous: {label: "Previous week", href: "?week=previous", props: {rel: "prev"}},
        next: {label: "Next week", href: "?week=next", props: {rel: "next"}},
        date: {group: {id: "calendar-clashing-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: Specimen_Reader.reader, value: Day(KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14})), today: KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14}), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-clashing-scope-form-group",
          kind: Select,
          label: React.string("Hall"),
          labelProps: {htmlFor: "calendar-clashing-scope"},
          controlProps: {id: "calendar-clashing-scope"},
          options: [
            {label: React.string("All halls"), value: "all", props: {}},
            {label: React.string("Oakmere Hall"), value: "oakmere", props: {}},
          ],
        }),
        FormGroup({id: "calendar-clashing-room-form-group",
          kind: Select,
          label: React.string("Room"),
          labelProps: {htmlFor: "calendar-clashing-room"},
          controlProps: {id: "calendar-clashing-room"},
          controlValue: "main-hall",
          options: [
            {label: React.string("All rooms"), value: "all", props: {}},
            {label: React.string("Main Hall"), value: "main-hall", props: {}},
          ],
        }),
      ]
      view={<KitReact.Roles.FormGroup id="calendar-clashing-view-form-group" label={React.string("View")} kind={KitReact.Roles.SegmentedControl.controlSet({name: "calendar-clashing-view", value: "week", options: Three({value: "week", content: React.string("Week")}, {value: "day", content: React.string("Day")}, {value: "month", content: React.string("Month")})})} />}
      actions={content: [PrimaryAction({children: React.string("New booking")})]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[
            head("2025-04-14", 14),
            head("2025-04-15", 15),
            head("2025-04-16", 16),
          ]
          hours=[
            hour(9),
            hour(10),
            hour(11),
            hour(12),
            hour(13),
          ]
          columns=[
            {
              key: "2025-04-14",
              children: <>
                <Calendar.Block
                  name={React.string("Toddler group")}
                  href="../../../record/"
                  span={span("2025-04-14T08:00:00Z", "2025-04-14T09:00:00Z")}
                  kind="Booking"
                  standings=[Clashing]
                  standingWords=["Time conflict"]
                  props={{ariaLabel: "Toddler group, 09:00–10:00, Time conflict", style: part(1, 1, 1)}}
                />
                <Calendar.Block
                  name={React.string("Hall inspection")}
                  href="../../../record/"
                  span={span("2025-04-14T08:30:00Z", "2025-04-14T09:00:00Z")}
                  kind="Inspection"
                  standings=[Clashing]
                  standingWords=["Time conflict"]
                  props={{ariaLabel: "Hall inspection, 09:30–10:00, Time conflict",
                    style: ReactDOM.Style.unsafeAddProp(
                      ReactDOM.Style.unsafeAddProp(
                        part(1, 1, 2),
                        "--kit-calendar-block-offset",
                        `calc(${hourHeight} / 2)`,
                      ),
                      "--kit-calendar-block-size",
                      `calc(${hourHeight} / 2)`,
                    ),
                  }}
                />
              </>,
              props: {style: parts(2)},
            },
            {
              key: "2025-04-15",
              children: <Calendar.Block
                name={React.string("Yoga class")}
                href="../../../record/"
                span={span("2025-04-15T09:00:00Z", "2025-04-15T10:00:00Z")}
                kind="Booking"
                props={{ariaLabel: "Yoga class, 10:00–11:00", style: placement(2, 1)}}
              />,
              props: {},
            },
            {
              key: "2025-04-16",
              children: <Calendar.Block
                name={React.string("Parish council")}
                href="../../../record/"
                span={span("2025-04-16T11:00:00Z", "2025-04-16T12:00:00Z")}
                kind="Meeting in 2 rooms"
                standings=[Clashing]
                standingWords=["Time conflict"]
                props={{ariaLabel: "Parish council, 12:00–13:00, Time conflict", style: placement(4, 1)}}
              />,
              props: {},
            },
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day
            day={day(14)}
            items=[
              item(
                ~key="bk-5501",
                ~name="Toddler group",
                ~span=span("2025-04-14T08:00:00Z", "2025-04-14T09:00:00Z"),
                ~kind="Booking",
                ~standings=[Clashing],
                ~standingWords=["Time conflict"],
              ),
              item(
                ~key="bk-5502",
                ~name="Hall inspection",
                ~span=span("2025-04-14T08:30:00Z", "2025-04-14T09:00:00Z"),
                ~kind="Inspection",
                ~standings=[Clashing],
                ~standingWords=["Time conflict"],
              ),
            ]
          />
          <Calendar.Day
            day={day(15)}
            items=[
              item(
                ~key="bk-5503",
                ~name="Yoga class",
                ~span=span("2025-04-15T09:00:00Z", "2025-04-15T10:00:00Z"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(16)}
            items=[
              item(
                ~key="bk-5504",
                ~name="Parish council",
                ~span=span("2025-04-16T11:00:00Z", "2025-04-16T12:00:00Z"),
                ~kind="Meeting in 2 rooms",
                ~standings=[Clashing],
                ~standingWords=["Time conflict"],
              ),
            ]
          />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, covered

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let reader = Specimen_Reader.reader

let day = (day): KitReact.Time.t => KitReact.Time.day(reader, {year: 2025, month: 4, day})

let span = (start, finish) =>
  KitReact.Time.interval(reader, ~start=Date.fromString(start), ~end=Date.fromString(finish))

let hour = (hour): DeskGrid.hour => {time: KitReact.Time.clock(reader, {hour, minute: 0}), props: {}}

let head = (key, on): DeskGrid.head => {key, column: Day(day(on)), props: {}}

let placement = (row, span) => (
  {gridRow: `${Int.toString(row)} / span ${Int.toString(span)}`}: ReactDOM.Style.t
)
let sized = (row, span, size) =>
  ReactDOM.Style.unsafeAddProp(placement(row, span), "--kit-calendar-block-size", size)

let hourHeight = "var(--kit-hour-height)"

let item = (~key, ~name, ~span, ~kind, ~standings=[], ~standingWords=[]): Calendar.itemRow => {
  key,
  name,
  href: Some("../../../record/"),
  span,
  kind,
  standings,
  standingWords,
  room: None,
  state: None,
  props: {},
}

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-covered"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-covered-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Calendar controls"}}
      range={
        days: KitReact.Time.dayRange(reader, ~first={year: 2025, month: 4, day: 14}, ~last={year: 2025, month: 4, day: 16}),
        today: {label: "Today", props: {}},
        previous: {label: "Previous week", href: "?week=previous", props: {rel: "prev"}},
        next: {label: "Next week", href: "?week=next", props: {rel: "next"}},
        date: {group: {id: "calendar-covered-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: Specimen_Reader.reader, value: Day(KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14})), today: KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14}), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-covered-scope-form-group",
          kind: Select,
          label: React.string("Hall"),
          labelProps: {htmlFor: "calendar-covered-scope"},
          controlProps: {id: "calendar-covered-scope"},
          options: [
            {label: React.string("All halls"), value: "all", props: {}},
            {label: React.string("Oakmere Hall"), value: "oakmere", props: {}},
          ],
        }),
        FormGroup({id: "calendar-covered-room-form-group",
          kind: Select,
          label: React.string("Room"),
          labelProps: {htmlFor: "calendar-covered-room"},
          controlProps: {id: "calendar-covered-room"},
          options: [
            {label: React.string("All rooms"), value: "all", props: {}},
            {label: React.string("Main Hall"), value: "main-hall", props: {}},
          ],
        }),
      ]
      view={<KitReact.Roles.FormGroup id="calendar-covered-view-form-group" label={React.string("View")} kind={KitReact.Roles.SegmentedControl.controlSet({name: "calendar-covered-view", value: "week", options: Three({value: "week", content: React.string("Week")}, {value: "day", content: React.string("Day")}, {value: "month", content: React.string("Month")})})} />}
      actions={content: [PrimaryAction({children: React.string("New booking")})]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[
            head("2025-04-14", 14),
            head("2025-04-15", 15),
            head("2025-04-16", 16),
          ]
          hours=[
            hour(9),
            hour(10),
            hour(11),
            hour(12),
            hour(13),
          ]
          columns=[
            {
              key: "2025-04-14",
              children: <Calendar.Block
                name={React.string("Toddler group")}
                href="../../../record/"
                span={span("2025-04-14T08:00:00Z", "2025-04-14T09:30:00Z")}
                kind="Booking"
                standings=[Covered]
                standingWords=["Cover arranged"]
                props={{ariaLabel: "Toddler group, 09:00–10:30, Cover arranged", style: sized(1, 2, `calc(${hourHeight} * 1.5)`)}}
              />,
              props: {},
            },
            {
              key: "2025-04-15",
              children: <Calendar.Block
                name={React.string("Yoga class")}
                href="../../../record/"
                span={span("2025-04-15T09:00:00Z", "2025-04-15T10:00:00Z")}
                kind="Booking"
                props={{ariaLabel: "Yoga class, 10:00–11:00", style: placement(2, 1)}}
              />,
              props: {},
            },
            {
              key: "2025-04-16",
              children: <Calendar.Block
                name={React.string("Community lunch")}
                href="../../../record/"
                span={span("2025-04-16T11:00:00Z", "2025-04-16T12:00:00Z")}
                kind="Booking"
                standings=[Covered]
                standingWords=["Cover arranged"]
                props={{ariaLabel: "Community lunch, 12:00–13:00, Cover arranged", style: placement(4, 1)}}
              />,
              props: {},
            },
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day
            day={day(14)}
            items=[
              item(
                ~key="bk-5201",
                ~name="Toddler group",
                ~span=span("2025-04-14T08:00:00Z", "2025-04-14T09:30:00Z"),
                ~kind="Booking",
                ~standings=[Covered],
                ~standingWords=["Cover arranged"],
              ),
            ]
          />
          <Calendar.Day
            day={day(15)}
            items=[
              item(
                ~key="bk-5202",
                ~name="Yoga class",
                ~span=span("2025-04-15T09:00:00Z", "2025-04-15T10:00:00Z"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(16)}
            items=[
              item(
                ~key="bk-5203",
                ~name="Community lunch",
                ~span=span("2025-04-16T11:00:00Z", "2025-04-16T12:00:00Z"),
                ~kind="Booking",
                ~standings=[Covered],
                ~standingWords=["Cover arranged"],
              ),
            ]
          />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, preparation

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let reader = Specimen_Reader.reader

let day = (day): KitReact.Time.t => KitReact.Time.day(reader, {year: 2025, month: 4, day})

let span = (start, finish) =>
  KitReact.Time.interval(reader, ~start=Date.fromString(start), ~end=Date.fromString(finish))

let hour = (hour): DeskGrid.hour => {time: KitReact.Time.clock(reader, {hour, minute: 0}), props: {}}

let head = (key, on): DeskGrid.head => {key, column: Day(day(on)), props: {}}

let placement = (row, span) => (
  {gridRow: `${Int.toString(row)} / span ${Int.toString(span)}`}: ReactDOM.Style.t
)
let sized = (row, span, size) =>
  ReactDOM.Style.unsafeAddProp(placement(row, span), "--kit-calendar-block-size", size)

let hourHeight = "var(--kit-hour-height)"

let item = (~key, ~name, ~span, ~kind, ~standings=[], ~standingWords=[]): Calendar.itemRow => {
  key,
  name,
  href: Some("../../../record/"),
  span,
  kind,
  standings,
  standingWords,
  room: None,
  state: None,
  props: {},
}

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-preparation"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-preparation-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Calendar controls"}}
      range={
        days: KitReact.Time.dayRange(reader, ~first={year: 2025, month: 4, day: 14}, ~last={year: 2025, month: 4, day: 16}),
        today: {label: "Today", props: {}},
        previous: {label: "Previous week", href: "?week=previous", props: {rel: "prev"}},
        next: {label: "Next week", href: "?week=next", props: {rel: "next"}},
        date: {group: {id: "calendar-preparation-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: Specimen_Reader.reader, value: Day(KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14})), today: KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14}), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-preparation-scope-form-group",
          kind: Select,
          label: React.string("Hall"),
          labelProps: {htmlFor: "calendar-preparation-scope"},
          controlProps: {id: "calendar-preparation-scope"},
          options: [
            {label: React.string("All halls"), value: "all", props: {}},
            {label: React.string("Oakmere Hall"), value: "oakmere", props: {}},
          ],
        }),
        FormGroup({id: "calendar-preparation-room-form-group",
          kind: Select,
          label: React.string("Room"),
          labelProps: {htmlFor: "calendar-preparation-room"},
          controlProps: {id: "calendar-preparation-room"},
          options: [
            {label: React.string("All rooms"), value: "all", props: {}},
            {label: React.string("Main Hall"), value: "main-hall", props: {}},
          ],
        }),
      ]
      view={<KitReact.Roles.FormGroup id="calendar-preparation-view-form-group" label={React.string("View")} kind={KitReact.Roles.SegmentedControl.controlSet({name: "calendar-preparation-view", value: "week", options: Three({value: "week", content: React.string("Week")}, {value: "day", content: React.string("Day")}, {value: "month", content: React.string("Month")})})} />}
      actions={content: [PrimaryAction({children: React.string("New booking")})]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[
            head("2025-04-14", 14),
            head("2025-04-15", 15),
            head("2025-04-16", 16),
          ]
          hours=[
            hour(9),
            hour(10),
            hour(11),
            hour(12),
            hour(13),
          ]
          columns=[
            {
              key: "2025-04-14",
              children: <Calendar.Block
                name={React.string("Summer term yoga")}
                href="../../../record/"
                span={span("2025-04-14T08:00:00Z", "2025-04-14T09:30:00Z")}
                kind="Booking"
                standings=[Preparation]
                standingWords=["Season not confirmed"]
                props={{ariaLabel: "Summer term yoga, 09:00–10:30, Season not confirmed", style: sized(1, 2, `calc(${hourHeight} * 1.5)`)}}
              />,
              props: {},
            },
            {
              key: "2025-04-15",
              children: <Calendar.Block
                name={React.string("Yoga class")}
                href="../../../record/"
                span={span("2025-04-15T09:00:00Z", "2025-04-15T10:00:00Z")}
                kind="Booking"
                props={{ariaLabel: "Yoga class, 10:00–11:00", style: placement(2, 1)}}
              />,
              props: {},
            },
            {
              key: "2025-04-16",
              children: <Calendar.Block
                name={React.string("Summer term toddler group")}
                href="../../../record/"
                span={span("2025-04-16T11:00:00Z", "2025-04-16T12:00:00Z")}
                kind="Booking"
                standings=[Preparation, Uncovered]
                standingWords=["Season not confirmed", "Cover needed"]
                props={{ariaLabel: "Summer term toddler group, 12:00–13:00, Season not confirmed, Cover needed", style: placement(4, 1)}}
              />,
              props: {},
            },
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day
            day={day(14)}
            items=[
              item(
                ~key="bk-5401",
                ~name="Summer term yoga",
                ~span=span("2025-04-14T08:00:00Z", "2025-04-14T09:30:00Z"),
                ~kind="Booking",
                ~standings=[Preparation],
                ~standingWords=["Season not confirmed"],
              ),
            ]
          />
          <Calendar.Day
            day={day(15)}
            items=[
              item(
                ~key="bk-5402",
                ~name="Yoga class",
                ~span=span("2025-04-15T09:00:00Z", "2025-04-15T10:00:00Z"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(16)}
            items=[
              item(
                ~key="bk-5403",
                ~name="Summer term toddler group",
                ~span=span("2025-04-16T11:00:00Z", "2025-04-16T12:00:00Z"),
                ~kind="Booking",
                ~standings=[Preparation, Uncovered],
                ~standingWords=["Season not confirmed", "Cover needed"],
              ),
            ]
          />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, day-rooms

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let reader = Specimen_Reader.reader

let day = (day): KitReact.Time.t => KitReact.Time.day(reader, {year: 2025, month: 4, day})

let span = (start, finish) =>
  KitReact.Time.interval(reader, ~start=Date.fromString(start), ~end=Date.fromString(finish))

let hour = (hour): DeskGrid.hour => {time: KitReact.Time.clock(reader, {hour, minute: 0}), props: {}}

let hours = [
  hour(9),
  hour(10),
  hour(11),
  hour(12),
  hour(13),
  hour(14),
]

let room = (key, name): DeskGrid.head => {
  key,
  column: Room(name),
  props: {},
}

let band = (name, first, last, stack): DeskGrid.band => {
  name: React.string(name),
  href: Some("../../../record/"),
  first,
  last,
  stack,
  props: {},
}

let placement = (row, span) => (
  {gridRow: `${Int.toString(row)} / span ${Int.toString(span)}`}: ReactDOM.Style.t
)

let halfHour = row =>
  placement(row, 1)
  ->ReactDOM.Style.unsafeAddProp("--kit-calendar-block-offset", "calc(var(--kit-hour-height) / 2)")
  ->ReactDOM.Style.unsafeAddProp("--kit-calendar-block-size", "calc(var(--kit-hour-height) / 2)")

let currentTime: DeskGrid.currentTime = {
  props: {
    style: ReactDOM.Style.unsafeAddProp(
      {},
      "--kit-calendar-now-offset",
      "calc(3.5 * var(--kit-hour-height))",
    ),
  },
}

let bandRow = (key, name): Calendar.bandRow => {
  key,
  name: React.string(name),
  href: Some("../../../record/"),
  state: None,
  props: {},
}

let item = (~key, ~name, ~span, ~kind, ~room=?): Calendar.itemRow => {
  key,
  name,
  href: Some("../../../record/"),
  span,
  kind,
  standings: [],
  standingWords: [],
  room,
  state: None,
  props: {},
}

let view = id =>
  <KitReact.Roles.FormGroup
    id={id ++ "-form-group"}
    label={React.string("View")}
    kind={KitReact.Roles.SegmentedControl.controlSet({name: id, value: "day", options: Three({value: "day", content: React.string("Day")}, {value: "week", content: React.string("Week")}, {value: "month", content: React.string("Month")})})}
  />

let newBooking =
  Calendar.PrimaryAction({children: React.string("New booking")})

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-day-rooms"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-day-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Calendar controls"}}
      range={
        days: day(14),
        today: {label: "Today", props: {}},
        previous: {label: "Previous day", href: "?day=previous", props: {rel: "prev"}},
        next: {label: "Next day", href: "?day=next", props: {rel: "next"}},
        date: {group: {id: "calendar-day-rooms-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: Specimen_Reader.reader, value: Day(KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14})), today: KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14}), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-day-scope-form-group",
          kind: Select,
          label: React.string("Hall"),
          labelProps: {htmlFor: "calendar-day-scope"},
          controlProps: {id: "calendar-day-scope"},
          options: [
            {label: React.string("All halls"), value: "all", props: {}},
            {label: React.string("Oakmere Hall"), value: "oakmere", props: {}},
          ],
        }),
        FormGroup({id: "calendar-day-room-form-group",
          kind: Select,
          label: React.string("Room"),
          labelProps: {htmlFor: "calendar-day-room"},
          controlProps: {id: "calendar-day-room"},
          options: [
            {label: React.string("All rooms"), value: "all", props: {}},
            {label: React.string("Main Hall"), value: "main-hall", props: {}},
            {
              label: React.string("Meeting Room"),
              value: "meeting-room",
              props: {},
            },
          ],
        }),
      ]
      view={view("calendar-day-view")}
      actions={content: [newBooking]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[
            room("unassigned", "No room yet"),
            room("main-hall", "Main Hall"),
            room("meeting-room", "Meeting Room"),
            room("memorial-room", "Memorial Room"),
          ]
          bands=[band("Open day", 1, 4, 1), band("Meeting Room decorated", 3, 3, 2)]
          hours
          currentTime
          columns=[
            {
              key: "unassigned",
              children: <Calendar.Block
                name={React.string("Hire request")}
                href="../../../record/"
                span={span("2025-04-14T10:00:00Z", "2025-04-14T11:00:00Z")}
                kind="Requested"
                props={{ariaLabel: "Hire request, 11:00–12:00", style: placement(3, 1)}}
              />,
              props: {},
            },
            {
              key: "main-hall",
              children: <>
                <Calendar.Block
                  name={React.string("Yoga class")}
                  href="../../../record/"
                  span={span("2025-04-14T09:00:00Z", "2025-04-14T10:00:00Z")}
                  kind="Booking"
                  props={{ariaLabel: "Yoga class, 10:00–11:00", style: placement(2, 1)}}
                />
                <Calendar.Block
                  name={React.string("Community lunch")}
                  href="../../../record/"
                  span={span("2025-04-14T10:30:00Z", "2025-04-14T11:00:00Z")}
                  kind="Booking"
                  props={{ariaLabel: "Community lunch, 11:30–12:00", style: halfHour(3)}}
                />
                <Calendar.Block
                  name={React.string("Wedding reception")}
                  href="../../../record/"
                  span={span("2025-04-14T11:00:00Z", "2025-04-14T14:00:00Z")}
                  kind="Booking"
                  props={{ariaLabel: "Wedding reception, 12:00–15:00", style: placement(4, 3)}}
                />
              </>,
              props: {},
            },
            {
              key: "meeting-room",
              children: <Calendar.Block
                name={React.string("Parish council")}
                href="../../../record/"
                span={span("2025-04-14T10:00:00Z", "2025-04-14T11:00:00Z")}
                kind="Meeting"
                props={{ariaLabel: "Parish council, 11:00–12:00", style: placement(3, 1)}}
              />,
              props: {},
            },
            {key: "memorial-room", children: React.null, props: {}},
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day
            day={day(14)}
            bands=[bandRow("ev-207", "Open day"), bandRow("wk-311", "Meeting Room decorated")]
            items=[
              item(
                ~key="bk-6301",
                ~name="Yoga class",
                ~span=span("2025-04-14T09:00:00Z", "2025-04-14T10:00:00Z"),
                ~kind="Booking",
                ~room="Main Hall",
              ),
              item(
                ~key="bk-6302",
                ~name="Hire request",
                ~span=span("2025-04-14T10:00:00Z", "2025-04-14T11:00:00Z"),
                ~kind="Requested",
              ),
              item(
                ~key="bk-6303",
                ~name="Parish council",
                ~span=span("2025-04-14T10:00:00Z", "2025-04-14T11:00:00Z"),
                ~kind="Meeting",
                ~room="Meeting Room",
              ),
              item(
                ~key="bk-6304",
                ~name="Community lunch",
                ~span=span("2025-04-14T10:30:00Z", "2025-04-14T11:00:00Z"),
                ~kind="Booking",
                ~room="Main Hall",
              ),
              item(
                ~key="bk-6305",
                ~name="Wedding reception",
                ~span=span("2025-04-14T11:00:00Z", "2025-04-14T14:00:00Z"),
                ~kind="Booking",
                ~room="Main Hall",
              ),
            ]
          />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, day-one-column

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let reader = Specimen_Reader.reader

let day = (day): KitReact.Time.t => KitReact.Time.day(reader, {year: 2025, month: 4, day})

let span = (start, finish) =>
  KitReact.Time.interval(reader, ~start=Date.fromString(start), ~end=Date.fromString(finish))

let hour = (hour): DeskGrid.hour => {time: KitReact.Time.clock(reader, {hour, minute: 0}), props: {}}

let currentTime: DeskGrid.currentTime = {
  props: {
    style: ReactDOM.Style.unsafeAddProp(
      {},
      "--kit-calendar-now-offset",
      "calc(3.5 * var(--kit-hour-height))",
    ),
  },
}

let hours = [
  hour(9),
  hour(10),
  hour(11),
  hour(12),
  hour(13),
  hour(14),
]

let placement = (row, span) => (
  {gridRow: `${Int.toString(row)} / span ${Int.toString(span)}`}: ReactDOM.Style.t
)

let item = (~key, ~name, ~span, ~kind, ~room=?): Calendar.itemRow => {
  key,
  name,
  href: Some("../../../record/"),
  span,
  kind,
  standings: [],
  standingWords: [],
  room,
  state: None,
  props: {},
}

let view = id =>
  <KitReact.Roles.FormGroup
    id={id ++ "-form-group"}
    label={React.string("View")}
    kind={KitReact.Roles.SegmentedControl.controlSet({name: id, value: "day", options: Three({value: "day", content: React.string("Day")}, {value: "week", content: React.string("Week")}, {value: "month", content: React.string("Month")})})}
  />

let newBooking =
  Calendar.PrimaryAction({children: React.string("New booking")})

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-day-one-column"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-day-one-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Memorial Hall calendar controls"}}
      range={
        days: day(14),
        today: {label: "Today", props: {}},
        previous: {label: "Previous day", href: "?day=previous", props: {rel: "prev"}},
        next: {label: "Next day", href: "?day=next", props: {rel: "next"}},
        date: {group: {id: "calendar-day-one-column-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: Specimen_Reader.reader, value: Day(KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14})), today: KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14}), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-day-one-scope-form-group",
          kind: Select,
          label: React.string("Hall"),
          labelProps: {htmlFor: "calendar-day-one-scope"},
          controlProps: {id: "calendar-day-one-scope"},
          options: [
            {label: React.string("Memorial Hall"), value: "memorial", props: {}},
          ],
        }),
        FormGroup({id: "calendar-day-one-kind-form-group",
          kind: Select,
          label: React.string("Kind"),
          labelProps: {htmlFor: "calendar-day-one-kind"},
          controlProps: {id: "calendar-day-one-kind"},
          options: [
            {label: React.string("All kinds"), value: "all", props: {}},
            {label: React.string("Bookings"), value: "booking", props: {}},
          ],
        }),
      ]
      view={view("calendar-day-one-view")}
      actions={content: [newBooking]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[{key: "2025-04-14", column: Day(day(14)), props: {}}]
          hours
          currentTime
          columns=[
            {
              key: "2025-04-14",
              children: <>
                <Calendar.Block
                  name={React.string("Art class")}
                  href="../../../record/"
                  span={span("2025-04-14T08:00:00Z", "2025-04-14T10:00:00Z")}
                  kind="Booking"
                  props={{ariaLabel: "Art class, 09:00–11:00", style: placement(1, 2)}}
                />
                <Calendar.Block
                  name={React.string("Whist drive")}
                  href="../../../record/"
                  span={span("2025-04-14T12:00:00Z", "2025-04-14T14:00:00Z")}
                  kind="Booking"
                  props={{ariaLabel: "Whist drive, 13:00–15:00", style: placement(5, 2)}}
                />
              </>,
              props: {},
            },
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day
            day={day(14)}
            items=[
              item(
                ~key="bk-6401",
                ~name="Art class",
                ~span=span("2025-04-14T08:00:00Z", "2025-04-14T10:00:00Z"),
                ~kind="Booking",
              ),
              item(
                ~key="bk-6402",
                ~name="Whist drive",
                ~span=span("2025-04-14T12:00:00Z", "2025-04-14T14:00:00Z"),
                ~kind="Booking",
              ),
            ]
          />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, month

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module MonthCell = Calendar.MonthCell

let reader = Specimen_Reader.reader

let on = (month, day): KitReact.Time.t => KitReact.Time.day(reader, {year: 2025, month, day})

let day = day => on(4, day)

let span = (start, finish) =>
  KitReact.Time.interval(reader, ~start=Date.fromString(start), ~end=Date.fromString(finish))

let booking = (name, start, standings): MonthCell.item => {
  name,
  href: "../../../record/",
  start: KitReact.Time.timeOfDay(reader, Date.fromString(start)),
  standings,
  props: {},
}

let item = (~key, ~name, ~span, ~kind, ~room=?, ~standings=[], ~standingWords=[]): Calendar.itemRow => {
  key,
  name,
  href: Some("../../../record/"),
  span,
  kind,
  standings,
  standingWords,
  room,
  state: None,
  props: {},
}

let weekdays =
  [
    ("monday", "Mon"),
    ("tuesday", "Tue"),
    ("wednesday", "Wed"),
    ("thursday", "Thu"),
    ("friday", "Fri"),
    ("saturday", "Sat"),
    ("sunday", "Sun"),
  ]->Array.map(((key, day)): Calendar.MonthGrid.head => {
    key,
    heading: <strong> {React.string(day)} </strong>,
    props: {},
  })

let number = date => KitReact.Time.dayNumber(reader, {year: 2025, month: 4, day: date})

let outside = (month, date) =>
  <MonthCell key={`${Int.toString(month)}-${Int.toString(date)}`} day={KitReact.Time.shortDate(reader, {year: 2025, month, day: date})} />

let plain = (date, month) =>
  <MonthCell
    key={Int.toString(date)}
    day={number(date)}
    new={`New booking on ${Int.toString(date)} ${month} 2025`}
  />

let one = (date, month, name, start) =>
  <MonthCell
    key={Int.toString(date)}
    day={number(date)}
    items=[booking(name, start, [])]
    new={`New booking on ${Int.toString(date)} ${month} 2025`}
  />

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-month"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-month-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Calendar controls"}}
      range={
        days: KitReact.Time.month(reader, ~year=2025, ~month=4),
        today: {label: "Today", props: {}},
        previous: {label: "Previous month", href: "?month=previous", props: {rel: "prev"}},
        next: {label: "Next month", href: "?month=next", props: {rel: "next"}},
        date: {group: {id: "calendar-month-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: Specimen_Reader.reader, value: Day(KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 1})), today: KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 1}), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-month-scope-form-group",
          kind: Select,
          label: React.string("Hall"),
          labelProps: {htmlFor: "calendar-month-scope"},
          controlProps: {id: "calendar-month-scope"},
          options: [
            {label: React.string("All halls"), value: "all", props: {}},
            {label: React.string("Oakmere Hall"), value: "oakmere", props: {}},
          ],
        }),
        FormGroup({id: "calendar-month-room-form-group",
          kind: Select,
          label: React.string("Room"),
          labelProps: {htmlFor: "calendar-month-room"},
          controlProps: {id: "calendar-month-room"},
          options: [
            {label: React.string("All rooms"), value: "all", props: {}},
            {label: React.string("Main Hall"), value: "main-hall", props: {}},
          ],
        }),
      ]
      view={<KitReact.Roles.FormGroup id="calendar-month-view-form-group" label={React.string("View")} kind={KitReact.Roles.SegmentedControl.controlSet({name: "calendar-month-view", value: "month", options: Three({value: "month", content: React.string("Month")}, {value: "week", content: React.string("Week")}, {value: "day", content: React.string("Day")})})} />}
      actions={content: [PrimaryAction({children: React.string("New booking")})]}
      content={subjectRegion => <>
        <Calendar.MonthGrid subjectRegion columns=7 columnHeads=weekdays>
          <>
            {outside(3, 31)}
            {one(1, "April", "Toddler group", "2025-04-01T08:30:00Z")}
            <MonthCell key="2" day={number(2)} />
            {one(3, "April", "Parish council", "2025-04-03T18:00:00Z")}
            {plain(4, "April")}
            {one(5, "April", "Birthday party", "2025-04-05T13:00:00Z")}
            {plain(6, "April")}
            {plain(7, "April")}
            {one(8, "April", "Toddler group", "2025-04-08T08:30:00Z")}
            {plain(9, "April")}
            {one(10, "April", "Yoga class", "2025-04-10T09:00:00Z")}
            {plain(11, "April")}
            {one(12, "April", "Wedding reception", "2025-04-12T11:00:00Z")}
            {plain(13, "April")}
            <MonthCell
              key="14"
              day={number(14)}
              bands=[
                {key: "ev-207", name: React.string("Open day"), href: "../../../record/", props: {}},
                {
                  key: "wk-311",
                  name: React.string("Meeting Room decorated"),
                  href: "../../../record/",
                  props: {},
                },
              ]
              items=[booking("Yoga class", "2025-04-14T09:00:00Z", [])]
              folded={{name: React.string("4 more"), href: "../day-rooms/", props: {}}}
            />
            <MonthCell
              key="15"
              day={number(15)}
              bands=[
                {
                  key: "cl-118",
                  name: React.string("Hall closed for rewiring"),
                  href: "../../../record/",
                  props: {},
                },
              ]
              items=[booking("Key handover", "2025-04-15T14:00:00Z", [])]
              new="New booking on 15 April 2025"
            />
            <MonthCell
              key="16"
              day={number(16)}
              items=[
                booking("Parish council", "2025-04-16T10:00:00Z", []),
                booking("Choir rehearsal", "2025-04-16T17:00:00Z", [Cancelled]),
              ]
              new="New booking on 16 April 2025"
            />
            {plain(17, "April")}
            {plain(18, "April")}
            {one(19, "April", "Birthday party", "2025-04-19T13:00:00Z")}
            {plain(20, "April")}
            {plain(21, "April")}
            {one(22, "April", "Toddler group", "2025-04-22T08:30:00Z")}
            {plain(23, "April")}
            {one(24, "April", "Yoga class", "2025-04-24T09:00:00Z")}
            {plain(25, "April")}
            {one(26, "April", "Village fair planning", "2025-04-26T09:00:00Z")}
            {plain(27, "April")}
            {plain(28, "April")}
            {one(29, "April", "Toddler group", "2025-04-29T08:30:00Z")}
            {plain(30, "April")}
            {outside(5, 1)}
            {outside(5, 2)}
            {outside(5, 3)}
            {outside(5, 4)}
          </>
        </Calendar.MonthGrid>
        <Calendar.Days subjectRegion>
          <Calendar.Day
            key="2025-04-01"
            day={day(1)}
            items=[
              item(
                ~key="bk-6501",
                ~name="Toddler group",
                ~span=span("2025-04-01T08:30:00Z", "2025-04-01T10:00:00Z"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            key="2025-04-02"
            day={day(2)}
            empty={<p> {React.string("Nothing booked")} </p>}
          />
          <Calendar.Day
            key="2025-04-03"
            day={day(3)}
            items=[
              item(
                ~key="bk-6502",
                ~name="Parish council",
                ~span=span("2025-04-03T18:00:00Z", "2025-04-03T19:30:00Z"),
                ~kind="Meeting",
              ),
            ]
          />
          <Calendar.Day
            key="2025-04-04"
            day={day(4)}
            empty={<p> {React.string("Nothing booked")} </p>}
          />
          <Calendar.Day
            key="2025-04-05"
            day={day(5)}
            items=[
              item(
                ~key="bk-6503",
                ~name="Birthday party",
                ~span=span("2025-04-05T13:00:00Z", "2025-04-05T15:00:00Z"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            key="2025-04-06"
            day={day(6)}
            empty={<p> {React.string("Nothing booked")} </p>}
          />
          <Calendar.Day
            key="2025-04-07"
            day={day(7)}
            empty={<p> {React.string("Nothing booked")} </p>}
          />
          <Calendar.Day
            key="2025-04-08"
            day={day(8)}
            items=[
              item(
                ~key="bk-6504",
                ~name="Toddler group",
                ~span=span("2025-04-08T08:30:00Z", "2025-04-08T10:00:00Z"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            key="2025-04-09"
            day={day(9)}
            empty={<p> {React.string("Nothing booked")} </p>}
          />
          <Calendar.Day
            key="2025-04-10"
            day={day(10)}
            items=[
              item(
                ~key="bk-6505",
                ~name="Yoga class",
                ~span=span("2025-04-10T09:00:00Z", "2025-04-10T10:00:00Z"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            key="2025-04-11"
            day={day(11)}
            empty={<p> {React.string("Nothing booked")} </p>}
          />
          <Calendar.Day
            key="2025-04-12"
            day={day(12)}
            items=[
              item(
                ~key="bk-6506",
                ~name="Wedding reception",
                ~span=span("2025-04-12T11:00:00Z", "2025-04-12T14:00:00Z"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            key="2025-04-13"
            day={day(13)}
            empty={<p> {React.string("Nothing booked")} </p>}
          />
          <Calendar.Day
            key="2025-04-14"
            day={day(14)}
            bands=[
              {
                key: "ev-207",
                name: React.string("Open day"),
                href: Some("../../../record/"),
                state: None,
                props: {},
              },
              {
                key: "wk-311",
                name: React.string("Meeting Room decorated"),
                href: Some("../../../record/"),
                state: None,
                props: {},
              },
            ]
            items=[
              item(
                ~key="bk-6507",
                ~name="Yoga class",
                ~span=span("2025-04-14T09:00:00Z", "2025-04-14T10:00:00Z"),
                ~kind="Booking",
                ~room="Main Hall",
              ),
              item(
                ~key="bk-6508",
                ~name="Hire request",
                ~span=span("2025-04-14T10:00:00Z", "2025-04-14T11:00:00Z"),
                ~kind="Requested",
              ),
              item(
                ~key="bk-6509",
                ~name="Parish council",
                ~span=span("2025-04-14T10:00:00Z", "2025-04-14T11:00:00Z"),
                ~kind="Meeting",
                ~room="Meeting Room",
              ),
              item(
                ~key="bk-6510",
                ~name="Community lunch",
                ~span=span("2025-04-14T10:30:00Z", "2025-04-14T11:00:00Z"),
                ~kind="Booking",
                ~room="Main Hall",
              ),
              item(
                ~key="bk-6511",
                ~name="Wedding reception",
                ~span=span("2025-04-14T11:00:00Z", "2025-04-14T14:00:00Z"),
                ~kind="Booking",
                ~room="Main Hall",
              ),
            ]
          />
          <Calendar.Day
            key="2025-04-15"
            day={day(15)}
            bands=[
              {
                key: "cl-118",
                name: React.string("Hall closed for rewiring"),
                href: Some("../../../record/"),
                state: None,
                props: {},
              },
            ]
            items=[
              item(
                ~key="bk-6512",
                ~name="Key handover",
                ~span=span("2025-04-15T14:00:00Z", "2025-04-15T14:20:00Z"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            key="2025-04-16"
            day={day(16)}
            items=[
              item(
                ~key="bk-6513",
                ~name="Parish council",
                ~span=span("2025-04-16T10:00:00Z", "2025-04-16T11:30:00Z"),
                ~kind="Meeting",
              ),
              item(
                ~key="bk-6514",
                ~name="Choir rehearsal",
                ~span=span("2025-04-16T17:00:00Z", "2025-04-16T19:00:00Z"),
                ~kind="Booking",
                ~standings=[Cancelled],
                ~standingWords=["Cancelled"],
              ),
            ]
          />
          <Calendar.Day
            key="2025-04-17"
            day={day(17)}
            empty={<p> {React.string("Nothing booked")} </p>}
          />
          <Calendar.Day
            key="2025-04-18"
            day={day(18)}
            empty={<p> {React.string("Nothing booked")} </p>}
          />
          <Calendar.Day
            key="2025-04-19"
            day={day(19)}
            items=[
              item(
                ~key="bk-6515",
                ~name="Birthday party",
                ~span=span("2025-04-19T13:00:00Z", "2025-04-19T15:00:00Z"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            key="2025-04-20"
            day={day(20)}
            empty={<p> {React.string("Nothing booked")} </p>}
          />
          <Calendar.Day
            key="2025-04-21"
            day={day(21)}
            empty={<p> {React.string("Nothing booked")} </p>}
          />
          <Calendar.Day
            key="2025-04-22"
            day={day(22)}
            items=[
              item(
                ~key="bk-6516",
                ~name="Toddler group",
                ~span=span("2025-04-22T08:30:00Z", "2025-04-22T10:00:00Z"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            key="2025-04-23"
            day={day(23)}
            empty={<p> {React.string("Nothing booked")} </p>}
          />
          <Calendar.Day
            key="2025-04-24"
            day={day(24)}
            items=[
              item(
                ~key="bk-6517",
                ~name="Yoga class",
                ~span=span("2025-04-24T09:00:00Z", "2025-04-24T10:00:00Z"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            key="2025-04-25"
            day={day(25)}
            empty={<p> {React.string("Nothing booked")} </p>}
          />
          <Calendar.Day
            key="2025-04-26"
            day={day(26)}
            items=[
              item(
                ~key="bk-6518",
                ~name="Village fair planning",
                ~span=span("2025-04-26T09:00:00Z", "2025-04-26T10:30:00Z"),
                ~kind="Meeting",
              ),
            ]
          />
          <Calendar.Day
            key="2025-04-27"
            day={day(27)}
            empty={<p> {React.string("Nothing booked")} </p>}
          />
          <Calendar.Day
            key="2025-04-28"
            day={day(28)}
            empty={<p> {React.string("Nothing booked")} </p>}
          />
          <Calendar.Day
            key="2025-04-29"
            day={day(29)}
            items=[
              item(
                ~key="bk-6519",
                ~name="Toddler group",
                ~span=span("2025-04-29T08:30:00Z", "2025-04-29T10:00:00Z"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            key="2025-04-30"
            day={day(30)}
            empty={<p> {React.string("Nothing booked")} </p>}
          />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, empty

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let reader = Specimen_Reader.reader

let hour = (on): DeskGrid.hour => {time: KitReact.Time.clock(reader, {hour: on, minute: 0}), props: {}}

let day = (on): KitReact.Time.t => KitReact.Time.day(reader, {year: 2025, month: 4, day: on})

let head = (on): DeskGrid.head => {key: Int.toString(on), column: Day(day(on)), props: {}}

let column = (key): DeskGrid.column => {key, children: React.null, props: {}}

let nothing = <p> {React.string("Nothing booked")} </p>

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-empty"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=Some(Empty({words: [KitReact.Words.Text("Nothing booked this week. Bookings show here once they're made.")]}))
      scopeId="kit-calendar-empty-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Calendar controls"}}
      range={
        days: KitReact.Time.dayRange(reader, ~first={year: 2025, month: 4, day: 14}, ~last={year: 2025, month: 4, day: 16}),
        today: {label: "Today", props: {}},
        previous: {label: "Previous week", href: "?week=previous", props: {rel: "prev"}},
        next: {label: "Next week", href: "?week=next", props: {rel: "next"}},
        date: {group: {id: "calendar-empty-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: Specimen_Reader.reader, value: Day(KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14})), today: KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14}), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-empty-scope-form-group",
          kind: Select,
          label: React.string("Hall"),
          labelProps: {htmlFor: "calendar-empty-scope"},
          controlProps: {id: "calendar-empty-scope"},
          options: [
            {label: React.string("All halls"), value: "all", props: {}},
            {label: React.string("Oakmere Hall"), value: "oakmere", props: {}},
          ],
        }),
        FormGroup({id: "calendar-empty-room-form-group",
          kind: Select,
          label: React.string("Room"),
          labelProps: {htmlFor: "calendar-empty-room"},
          controlProps: {id: "calendar-empty-room"},
          options: [
            {label: React.string("All rooms"), value: "all", props: {}},
            {label: React.string("Main Hall"), value: "main-hall", props: {}},
          ],
        }),
      ]
      view={<KitReact.Roles.FormGroup id="calendar-empty-view-form-group" label={React.string("View")} kind={KitReact.Roles.SegmentedControl.controlSet({name: "calendar-empty-view", value: "week", options: Three({value: "week", content: React.string("Week")}, {value: "day", content: React.string("Day")}, {value: "month", content: React.string("Month")})})} />}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[
            head(14),
            head(15),
            head(16),
          ]
          hours=[
            hour(7),
            hour(8),
            hour(9),
            hour(10),
            hour(11),
            hour(12),
            hour(13),
            hour(14),
            hour(15),
            hour(16),
            hour(17),
            hour(18),
            hour(19),
            hour(20),
          ]
          columns=[column("2025-04-14"), {...column("2025-04-15"), children: <Calendar.CurrentTime props={{style: ReactDOM.Style.unsafeAddProp({}, "--kit-calendar-now-offset", "calc(11.5 * var(--kit-hour-height))")}} />}, column("2025-04-16")]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day day={day(14)} empty=nothing />
          <Calendar.Day day={day(15)} empty=nothing />
          <Calendar.Day day={day(16)} empty=nothing />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, moving

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let reader = Specimen_Reader.reader

let newBooking =
  Calendar.PrimaryAction({children: React.string("New booking")})

let hour = (on): DeskGrid.hour => {time: KitReact.Time.clock(reader, {hour: on, minute: 0}), props: {}}

let hours = [
  hour(8),
  hour(9),
  hour(10),
  hour(11),
  hour(12),
  hour(13),
  hour(14),
  hour(15),
  hour(16),
  hour(17),
]

let day = (on): KitReact.Time.t => KitReact.Time.day(reader, {year: 2025, month: 4, day: on})

let head = (on): DeskGrid.head => {key: Int.toString(on), column: Day(day(on)), props: {}}

let placement = (row, span) => (
  {gridRow: `${Int.toString(row)} / span ${Int.toString(span)}`}: ReactDOM.Style.t
)

let item = (~key, ~href="../../../record/", ~name, ~span, ~kind): Calendar.itemRow => {
  key,
  name,
  href: Some(href),
  span,
  kind,
  standings: [],
  standingWords: [],
  room: None,
  state: None,
  props: {},
}

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-moving"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-moving-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Calendar controls"}}
      range={
        days: KitReact.Time.dayRange(reader, ~first={year: 2025, month: 4, day: 14}, ~last={year: 2025, month: 4, day: 17}),
        today: {label: "Today", props: {}},
        previous: {label: "Previous week", href: "?week=previous", props: {rel: "prev"}},
        next: {label: "Next week", href: "?week=next", props: {rel: "next"}},
        date: {group: {id: "calendar-moving-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: Specimen_Reader.reader, value: Day(KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14})), today: KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14}), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-moving-scope-form-group", kind: Select,
          label: React.string("Hall"),
          labelProps: {htmlFor: "calendar-moving-scope"},
          controlProps: {id: "calendar-moving-scope"},
          options: [
            {label: React.string("All halls"), value: "all", props: {}},
            {label: React.string("Oakmere Hall"), value: "oakmere", props: {}},
          ],
        }),
        FormGroup({id: "calendar-moving-room-form-group", kind: Select,
          label: React.string("Room"),
          labelProps: {htmlFor: "calendar-moving-room"},
          controlProps: {id: "calendar-moving-room"},
          options: [
            {label: React.string("All rooms"), value: "all", props: {}},
            {label: React.string("Main Hall"), value: "main-hall", props: {}},
          ],
        }),
      ]
      view={<KitReact.Roles.FormGroup id="calendar-moving-view-form-group" label={React.string("View")} kind={KitReact.Roles.SegmentedControl.controlSet({name: "calendar-moving-view", value: "week", options: Three({value: "week", content: React.string("Week")}, {value: "day", content: React.string("Day")}, {value: "month", content: React.string("Month")})})} />}
      actions={content: [newBooking]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[
            head(14),
            head(15),
            head(16),
            head(17),
          ]
          hours
          columns=[
            {
              key: "2025-04-14",
              children: <Calendar.Block
                name={React.string("Board meeting")}
                href="../../../record/"
                span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-14T08:00Z"), ~end=Date.fromString("2025-04-14T09:00Z"))
                kind="Meeting"
                props={{ariaLabel: "Board meeting, 09:00–10:00", style: placement(2, 1)}}
              />,
              props: {},
            },
            {
              key: "2025-04-15",
              children: <Calendar.Block
                name={React.string("Yoga class")}
                href="../../../record/calendar-item/"
                span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-15T09:00Z"), ~end=Date.fromString("2025-04-15T11:00Z"))
                kind="Booking"
                moving=true
                props={{ariaLabel: "Yoga class, 10:00–12:00", draggable: true, style: placement(3, 2)}}
              />,
              props: {},
            },
            {
              key: "2025-04-16",
              children: <Calendar.Block
                name={React.string("Parish council")}
                href="../../../record/"
                span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-16T10:00Z"), ~end=Date.fromString("2025-04-16T11:00Z"))
                kind="Meeting"
                props={{ariaLabel: "Parish council, 11:00–12:00", style: placement(4, 1)}}
              />,
              props: {},
            },
            {
              key: "2025-04-17",
              children: <Calendar.Block
                name={React.string("Wedding reception")}
                href="../../../record/"
                span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-17T11:00Z"), ~end=Date.fromString("2025-04-17T14:00Z"))
                kind="Booking"
                props={{ariaLabel: "Wedding reception, 12:00–15:00", style: placement(5, 3)}}
              />,
              props: {},
            },
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day
            day={day(14)}
            items=[
              item(
                ~key="bk-5701",
                ~name="Board meeting",
                ~span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-14T08:00Z"), ~end=Date.fromString("2025-04-14T09:00Z")),
                ~kind="Meeting",
              ),
            ]
          />
          <Calendar.Day
            day={day(15)}
            items=[
              item(
                ~key="bk-5702",
                ~name="Yoga class",
                ~href="../../../record/calendar-item/",
                ~span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-15T09:00Z"), ~end=Date.fromString("2025-04-15T11:00Z")),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(16)}
            items=[
              item(
                ~key="bk-5703",
                ~name="Parish council",
                ~span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-16T10:00Z"), ~end=Date.fromString("2025-04-16T11:00Z")),
                ~kind="Meeting",
              ),
            ]
          />
          <Calendar.Day
            day={day(17)}
            items=[
              item(
                ~key="bk-5704",
                ~name="Wedding reception",
                ~span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-17T11:00Z"), ~end=Date.fromString("2025-04-17T14:00Z")),
                ~kind="Booking",
              ),
            ]
          />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, accepting

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let reader = Specimen_Reader.reader

let newBooking =
  Calendar.PrimaryAction({children: React.string("New booking")})

let hour = (on): DeskGrid.hour => {time: KitReact.Time.clock(reader, {hour: on, minute: 0}), props: {}}

let hours = [
  hour(8),
  hour(9),
  hour(10),
  hour(11),
  hour(12),
  hour(13),
  hour(14),
  hour(15),
  hour(16),
  hour(17),
]

let day = (on): KitReact.Time.t => KitReact.Time.day(reader, {year: 2025, month: 4, day: on})

let head = (on): DeskGrid.head => {key: Int.toString(on), column: Day(day(on)), props: {}}

let placement = (row, span) => (
  {gridRow: `${Int.toString(row)} / span ${Int.toString(span)}`}: ReactDOM.Style.t
)

let item = (~key, ~href="../../../record/", ~name, ~span, ~kind): Calendar.itemRow => {
  key,
  name,
  href: Some(href),
  span,
  kind,
  standings: [],
  standingWords: [],
  room: None,
  state: None,
  props: {},
}

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-accepting"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-accepting-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Calendar controls"}}
      range={
        days: KitReact.Time.dayRange(reader, ~first={year: 2025, month: 4, day: 14}, ~last={year: 2025, month: 4, day: 17}),
        today: {label: "Today", props: {}},
        previous: {label: "Previous week", href: "?week=previous", props: {rel: "prev"}},
        next: {label: "Next week", href: "?week=next", props: {rel: "next"}},
        date: {group: {id: "calendar-accepting-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: Specimen_Reader.reader, value: Day(KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14})), today: KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14}), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-accepting-scope-form-group", kind: Select,
          label: React.string("Hall"),
          labelProps: {htmlFor: "calendar-accepting-scope"},
          controlProps: {id: "calendar-accepting-scope"},
          options: [
            {label: React.string("All halls"), value: "all", props: {}},
            {label: React.string("Oakmere Hall"), value: "oakmere", props: {}},
          ],
        }),
        FormGroup({id: "calendar-accepting-room-form-group", kind: Select,
          label: React.string("Room"),
          labelProps: {htmlFor: "calendar-accepting-room"},
          controlProps: {id: "calendar-accepting-room"},
          options: [
            {label: React.string("All rooms"), value: "all", props: {}},
            {label: React.string("Main Hall"), value: "main-hall", props: {}},
          ],
        }),
      ]
      view={<KitReact.Roles.FormGroup id="calendar-accepting-view-form-group" label={React.string("View")} kind={KitReact.Roles.SegmentedControl.controlSet({name: "calendar-accepting-view", value: "week", options: Three({value: "week", content: React.string("Week")}, {value: "day", content: React.string("Day")}, {value: "month", content: React.string("Month")})})} />}
      actions={content: [newBooking]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[
            head(14),
            head(15),
            head(16),
            head(17),
          ]
          hours
          columns=[
            {
              key: "2025-04-14",
              children: <Calendar.Block
                name={React.string("Board meeting")}
                href="../../../record/"
                span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-14T08:00Z"), ~end=Date.fromString("2025-04-14T09:00Z"))
                kind="Meeting"
                props={{ariaLabel: "Board meeting, 09:00–10:00", style: placement(2, 1)}}
              />,
              props: {},
            },
            {
              key: "2025-04-15",
              children: <Calendar.Block
                name={React.string("Yoga class")}
                href="../../../record/calendar-item/"
                span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-15T09:00Z"), ~end=Date.fromString("2025-04-15T11:00Z"))
                kind="Booking"
                moving=true
                props={{ariaLabel: "Yoga class, 10:00–12:00", draggable: true, style: placement(3, 2)}}
              />,
              props: {},
            },
            {
              key: "2025-04-16",
              children: <Calendar.Block
                name={React.string("Parish council")}
                href="../../../record/"
                span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-16T10:00Z"), ~end=Date.fromString("2025-04-16T11:00Z"))
                kind="Meeting"
                props={{ariaLabel: "Parish council, 11:00–12:00", style: placement(4, 1)}}
              />,
              props: {},
            },
            {
              key: "2025-04-17",
              children: <>
                <Calendar.DropTarget
                  preview={{
                    name: React.string("Yoga class"),
                    span: KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-17T09:00Z"), ~end=Date.fromString("2025-04-17T11:00Z")),
                    kind: "Booking",
                  }}
                  standing=Accepting
                  props={{style: placement(3, 2)}}
                />
                <Calendar.Block
                  name={React.string("Wedding reception")}
                  href="../../../record/"
                  span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-17T11:00Z"), ~end=Date.fromString("2025-04-17T14:00Z"))
                  kind="Booking"
                  props={{ariaLabel: "Wedding reception, 12:00–15:00", style: placement(5, 3)}}
                />
              </>,
              props: {},
            },
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day
            day={day(14)}
            items=[
              item(
                ~key="bk-5601",
                ~name="Board meeting",
                ~span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-14T08:00Z"), ~end=Date.fromString("2025-04-14T09:00Z")),
                ~kind="Meeting",
              ),
            ]
          />
          <Calendar.Day
            day={day(15)}
            items=[
              item(
                ~key="bk-5602",
                ~name="Yoga class",
                ~href="../../../record/calendar-item/",
                ~span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-15T09:00Z"), ~end=Date.fromString("2025-04-15T11:00Z")),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(16)}
            items=[
              item(
                ~key="bk-5603",
                ~name="Parish council",
                ~span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-16T10:00Z"), ~end=Date.fromString("2025-04-16T11:00Z")),
                ~kind="Meeting",
              ),
            ]
          />
          <Calendar.Day
            day={day(17)}
            items=[
              item(
                ~key="bk-5604",
                ~name="Wedding reception",
                ~span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-17T11:00Z"), ~end=Date.fromString("2025-04-17T14:00Z")),
                ~kind="Booking",
              ),
            ]
          />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, refused

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let reader = Specimen_Reader.reader

let newBooking =
  Calendar.PrimaryAction({children: React.string("New booking")})

let hour = (on): DeskGrid.hour => {time: KitReact.Time.clock(reader, {hour: on, minute: 0}), props: {}}

let hours = [
  hour(8),
  hour(9),
  hour(10),
  hour(11),
  hour(12),
  hour(13),
  hour(14),
  hour(15),
  hour(16),
  hour(17),
]

let day = (on): KitReact.Time.t => KitReact.Time.day(reader, {year: 2025, month: 4, day: on})

let head = (on): DeskGrid.head => {key: Int.toString(on), column: Day(day(on)), props: {}}

let placement = (row, span) => (
  {gridRow: `${Int.toString(row)} / span ${Int.toString(span)}`}: ReactDOM.Style.t
)

let yoga = (span): Calendar.preview => {
  name: React.string("Yoga class"),
  span,
  kind: "Booking",
}

let item = (~key, ~href="../../../record/", ~name, ~span, ~kind): Calendar.itemRow => {
  key,
  name,
  href: Some(href),
  span,
  kind,
  standings: [],
  standingWords: [],
  room: None,
  state: None,
  props: {},
}

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-refused"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-refused-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Calendar controls"}}
      range={
        days: KitReact.Time.dayRange(reader, ~first={year: 2025, month: 4, day: 14}, ~last={year: 2025, month: 4, day: 17}),
        today: {label: "Today", props: {}},
        previous: {label: "Previous week", href: "?week=previous", props: {rel: "prev"}},
        next: {label: "Next week", href: "?week=next", props: {rel: "next"}},
        date: {group: {id: "calendar-refused-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: Specimen_Reader.reader, value: Day(KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14})), today: KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14}), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-refused-scope-form-group", kind: Select,
          label: React.string("Hall"),
          labelProps: {htmlFor: "calendar-refused-scope"},
          controlProps: {id: "calendar-refused-scope"},
          options: [
            {label: React.string("All halls"), value: "all", props: {}},
            {label: React.string("Oakmere Hall"), value: "oakmere", props: {}},
          ],
        }),
        FormGroup({id: "calendar-refused-room-form-group", kind: Select,
          label: React.string("Room"),
          labelProps: {htmlFor: "calendar-refused-room"},
          controlProps: {id: "calendar-refused-room"},
          options: [
            {label: React.string("All rooms"), value: "all", props: {}},
            {label: React.string("Main Hall"), value: "main-hall", props: {}},
          ],
        }),
      ]
      view={<KitReact.Roles.FormGroup id="calendar-refused-view-form-group" label={React.string("View")} kind={KitReact.Roles.SegmentedControl.controlSet({name: "calendar-refused-view", value: "week", options: Three({value: "week", content: React.string("Week")}, {value: "day", content: React.string("Day")}, {value: "month", content: React.string("Month")})})} />}
      actions={content: [newBooking]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[
            head(14),
            head(15),
            head(16),
            head(17),
          ]
          hours
          columns=[
            {
              key: "2025-04-14",
              children: <>
                <Calendar.Block
                  name={React.string("Board meeting")}
                  href="../../../record/"
                  span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-14T08:00Z"), ~end=Date.fromString("2025-04-14T09:00Z"))
                  kind="Meeting"
                  props={{ariaLabel: "Board meeting, 09:00–10:00", style: placement(2, 1)}}
                />
                <Calendar.DropTarget
                  preview={yoga(KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-14T14:00Z"), ~end=Date.fromString("2025-04-14T16:00Z")))}
                  standing=Refused
                  reason=?Some({content: React.string("Oakmere Hall is kept for the lunch club then"), side: Trailing})
                  props={{style: placement(8, 2)}}
                />
              </>,
              props: {},
            },
            {
              key: "2025-04-15",
              children: <Calendar.Block
                name={React.string("Yoga class")}
                href="../../../record/calendar-item/"
                span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-15T09:00Z"), ~end=Date.fromString("2025-04-15T11:00Z"))
                kind="Booking"
                moving=true
                props={{ariaLabel: "Yoga class, 10:00–12:00", draggable: true, style: placement(3, 2)}}
              />,
              props: {},
            },
            {
              key: "2025-04-16",
              children: <>
                <Calendar.Block
                  name={React.string("Parish council")}
                  href="../../../record/"
                  span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-16T10:00Z"), ~end=Date.fromString("2025-04-16T11:00Z"))
                  kind="Meeting"
                  props={{ariaLabel: "Parish council, 11:00–12:00", style: placement(4, 1)}}
                />
                <Calendar.DropTarget
                  preview={yoga(KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-16T12:00Z"), ~end=Date.fromString("2025-04-16T13:00Z")))}
                  standing=Refused
                  reason=?Some({content: React.string("Oakmere Hall is closed for floor cleaning then"), side: Below})
                  props={{style: placement(6, 1)}}
                />
              </>,
              props: {},
            },
            {
              key: "2025-04-17",
              children: <>
                <Calendar.DropTarget
                  preview={yoga(KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-17T09:00Z"), ~end=Date.fromString("2025-04-17T11:00Z")))}
                  standing=Refused
                  reason=?Some({content: <>{React.string("Oakmere Hall closes at ")}<time dateTime="10:00">{React.string("10:00")}</time>{React.string(" on Thursdays")}</>, side: Leading})
                  props={{style: placement(3, 2)}}
                />
                <Calendar.Block
                  name={React.string("Wedding reception")}
                  href="../../../record/"
                  span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-17T11:00Z"), ~end=Date.fromString("2025-04-17T14:00Z"))
                  kind="Booking"
                  props={{ariaLabel: "Wedding reception, 12:00–15:00", style: placement(5, 3)}}
                />
              </>,
              props: {},
            },
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day
            day={day(14)}
            items=[
              item(
                ~key="bk-5801",
                ~name="Board meeting",
                ~span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-14T08:00Z"), ~end=Date.fromString("2025-04-14T09:00Z")),
                ~kind="Meeting",
              ),
            ]
          />
          <Calendar.Day
            day={day(15)}
            items=[
              item(
                ~key="bk-5802",
                ~name="Yoga class",
                ~href="../../../record/calendar-item/",
                ~span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-15T09:00Z"), ~end=Date.fromString("2025-04-15T11:00Z")),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(16)}
            items=[
              item(
                ~key="bk-5803",
                ~name="Parish council",
                ~span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-16T10:00Z"), ~end=Date.fromString("2025-04-16T11:00Z")),
                ~kind="Meeting",
              ),
            ]
          />
          <Calendar.Day
            day={day(17)}
            items=[
              item(
                ~key="bk-5804",
                ~name="Wedding reception",
                ~span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-17T11:00Z"), ~end=Date.fromString("2025-04-17T14:00Z")),
                ~kind="Booking",
              ),
            ]
          />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, toolbar-form

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

@module({from: "kit/react/locales/en.json", with: {type_: "json"}})
external en: KitReact.Locale.t = "default"

let reader = KitReact.Reader.make(~locale=en, ~zone="Europe/London")

let hour = (on): DeskGrid.hour => {time: KitReact.Time.clock(reader, {hour: on, minute: 0}), props: {}}

let day = (on): KitReact.Time.t => KitReact.Time.day(reader, {year: 2025, month: 4, day: on})

let head = (on): DeskGrid.head => {key: Int.toString(on), column: Day(day(on)), props: {}}

let placement = (row, span) => (
  {gridRow: `${Int.toString(row)} / span ${Int.toString(span)}`}: ReactDOM.Style.t
)

let item = (~key, ~name, ~span, ~kind): Calendar.itemRow => {
  key,
  name,
  href: Some("../../../record/"),
  span,
  kind,
  standings: [],
  standingWords: [],
  room: None,
  state: None,
  props: {},
}

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-toolbar-form"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-toolbar-form-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Calendar controls"}}
      toolbarFormProps=?{Some(({}: JsxDOM.domProps))}
      range={
        days: KitReact.Time.week(reader, {year: 2025, month: 4, day: 14}),
        today: {label: "Today", props: {}},
        previous: {label: "Previous week", href: "?week=previous", props: {rel: "prev"}},
        next: {label: "Next week", href: "?week=next", props: {rel: "next"}},
        date: {group: {id: "calendar-toolbar-form-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: reader, value: Day(KitReact.Time.day(reader, {year: 2025, month: 4, day: 14})), today: KitReact.Time.day(reader, {year: 2025, month: 4, day: 14}), direction: Either})}, formProps: {}},
      }
      scope=[
        FormGroup({id: "calendar-toolbar-form-scope-form-group",
          kind: Select,
          label: React.string("Hall"),
          labelProps: {htmlFor: "calendar-toolbar-form-scope"},
          controlProps: {id: "calendar-toolbar-form-scope", name: "hall"},
          controlValue: "oakmere",
          options: [
            {label: React.string("All halls"), value: "all", props: {}},
            {label: React.string("Oakmere Hall"), value: "oakmere", props: {}},
          ],
        }),
        FormGroup({id: "calendar-toolbar-form-room-form-group",
          kind: Select,
          label: React.string("Room"),
          labelProps: {htmlFor: "calendar-toolbar-form-room"},
          controlProps: {id: "calendar-toolbar-form-room", name: "room"},
          options: [
            {label: React.string("All rooms"), value: "all", props: {}},
            {label: React.string("Main Hall"), value: "main-hall", props: {}},
          ],
        }),
      ]
      view={<KitReact.Roles.FormGroup id="calendar-toolbar-form-view" label={React.string("View")} kind={KitReact.Roles.SegmentedControl.controlSet({name: "view", value: "week", options: Three({value: "week", content: React.string("Week")}, {value: "day", content: React.string("Day")}, {value: "month", content: React.string("Month")})})} />}
      actions={content: [PrimaryAction({children: React.string("New booking")})]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[head(13), head(14), head(15), head(16), head(17), head(18), head(19)]
          hours=[
            hour(9),
            hour(10),
            hour(11),
            hour(12),
            hour(13),
          ]
          columns=[
            {key: "2025-04-13", children: React.null, props: {}},
            {
              key: "2025-04-14",
              children: <Calendar.Block
                name={React.string("Hall hire")}
                href="../../../record/"
                span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-14T08:00Z"), ~end=Date.fromString("2025-04-14T10:00Z"))
                kind="Booking"
                props={{ariaLabel: "Hall hire, 9:00–11:00 AM", style: placement(1, 2)}}
              />,
              props: {},
            },
            {
              key: "2025-04-15",
              children: <Calendar.Block
                name={React.string("Yoga class")}
                href="../../../record/"
                span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-15T10:00Z"), ~end=Date.fromString("2025-04-15T11:00Z"))
                kind="Booking"
                props={{ariaLabel: "Yoga class, 11:00 AM–12:00 PM", style: placement(3, 1)}}
              />,
              props: {},
            },
            {
              key: "2025-04-16",
              children: <Calendar.Block
                name={React.string("Committee meeting")}
                href="../../../record/"
                span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-16T12:00Z"), ~end=Date.fromString("2025-04-16T13:00Z"))
                kind="Booking"
                props={{ariaLabel: "Committee meeting, 1:00–2:00 PM", style: placement(5, 1)}}
              />,
              props: {},
            },
            {key: "2025-04-17", children: React.null, props: {}},
            {key: "2025-04-18", children: React.null, props: {}},
            {key: "2025-04-19", children: React.null, props: {}},
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day
            day={day(13)}
            empty={<p> {React.string("Nothing booked")} </p>}
          />
          <Calendar.Day
            day={day(14)}
            items=[
              item(
                ~key="bk-7101",
                ~name="Hall hire",
                ~span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-14T08:00Z"), ~end=Date.fromString("2025-04-14T10:00Z")),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(15)}
            items=[
              item(
                ~key="bk-7102",
                ~name="Yoga class",
                ~span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-15T10:00Z"), ~end=Date.fromString("2025-04-15T11:00Z")),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(16)}
            items=[
              item(
                ~key="bk-7103",
                ~name="Committee meeting",
                ~span=KitReact.Time.interval(reader, ~start=Date.fromString("2025-04-16T12:00Z"), ~end=Date.fromString("2025-04-16T13:00Z")),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day
            day={day(17)}
            empty={<p> {React.string("Nothing booked")} </p>}
          />
          <Calendar.Day
            day={day(18)}
            empty={<p> {React.string("Nothing booked")} </p>}
          />
          <Calendar.Day
            day={day(19)}
            empty={<p> {React.string("Nothing booked")} </p>}
          />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, at-place

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let reader = KitReact.Reader.atPlace(Specimen_Reader.reader, ~zone="America/New_York")

let date = (on): KitReact.Time.t => KitReact.Time.day(reader, {year: 2025, month: 4, day: on})

let range = KitReact.Time.dayRange(reader, ~first={year: 2025, month: 4, day: 14}, ~last={year: 2025, month: 4, day: 20})

let span = (~place=?, start, finish) =>
  KitReact.Time.interval(reader, ~start=Date.fromString(start), ~end=Date.fromString(finish), ~place?)

let yoga = span("2025-04-14T13:00Z", "2025-04-14T14:00Z")
let pilates = span("2025-04-16T15:00Z", "2025-04-16T16:00Z")
let supplierCall = span("2025-04-17T14:00Z", "2025-04-17T15:00Z", ~place="Europe/London")
let danceRehearsal = span("2025-04-19T19:00Z", "2025-04-19T20:00Z")

let hour = (on): DeskGrid.hour => {time: KitReact.Time.clock(reader, {hour: on, minute: 0}), props: {}}

let head = (on): DeskGrid.head => {key: KitReact.Time.datetime(date(on)), column: Day(date(on)), props: {}}

let placement = (row, span) => (
  {gridRow: `${Int.toString(row)} / span ${Int.toString(span)}`}: ReactDOM.Style.t
)

let column = (on, children): DeskGrid.column => {
  key: KitReact.Time.datetime(date(on)),
  children,
    props: {},
}

let block = (~name, ~ariaLabel, ~span, ~kind, ~row) =>
  <Calendar.Block
    name={React.string(name)} href="../../../record/" span kind props={{ariaLabel, style: placement(row, 1)}}
  />

let item = (~key, ~name, ~span, ~kind): Calendar.itemRow => {
  key,
  name,
  href: Some("../../../record/"),
  span,
  kind,
  standings: [],
  standingWords: [],
  room: None,
  state: None,
  props: {},
}

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-at-place"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-at-place-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Calendar controls"}}
      range={
        days: range,
        today: {label: "Today", props: {}},
        previous: {label: "Previous week", href: "?week=previous", props: {rel: "prev"}},
        next: {label: "Next week", href: "?week=next", props: {rel: "next"}},
        date: {group: {id: "calendar-at-place-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: Specimen_Reader.reader, value: Day(KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14})), today: KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14}), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-at-place-scope-form-group",
          kind: Select,
          label: React.string("Studio"),
          labelProps: {htmlFor: "calendar-at-place-scope"},
          controlProps: {id: "calendar-at-place-scope"},
          options: [
            {label: React.string("All studios"), value: "all", props: {}},
            {label: React.string("Riverside Studio"), value: "riverside", props: {}},
          ],
        }),
      ]
      view={<KitReact.Roles.FormGroup id="calendar-at-place-view-form-group" label={React.string("View")} kind={KitReact.Roles.SegmentedControl.controlSet({name: "calendar-at-place-view", value: "week", options: Three({value: "week", content: React.string("Week")}, {value: "day", content: React.string("Day")}, {value: "month", content: React.string("Month")})})} />}
      actions={content: [PrimaryAction({children: React.string("New booking")})]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[head(14), head(15), head(16), head(17), head(18), head(19), head(20)]
          hours=[hour(8), hour(9), hour(10), hour(11), hour(12), hour(13), hour(14), hour(15), hour(16), hour(17)]
          columns=[
            column(14, block(~name="Yoga class", ~ariaLabel="Yoga class, 09:00–10:00", ~span=yoga, ~kind="Booking", ~row=2)),
            column(15, React.null),
            column(16, block(~name="Pilates", ~ariaLabel="Pilates, 11:00–12:00", ~span=pilates, ~kind="Booking", ~row=4)),
            column(17, block(~name="Supplier call", ~ariaLabel="Supplier call, 15:00–16:00 UK Time", ~span=supplierCall, ~kind="Call", ~row=3)),
            column(18, React.null),
            column(19, block(~name="Dance rehearsal", ~ariaLabel="Dance rehearsal, 15:00–16:00", ~span=danceRehearsal, ~kind="Booking", ~row=8)),
            column(20, React.null),
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day day={date(14)} items=[item(~key="yoga-class", ~name="Yoga class", ~span=yoga, ~kind="Booking")] />
          <Calendar.Day day={date(15)} empty={<p> {React.string("Nothing booked")} </p>} />
          <Calendar.Day day={date(16)} items=[item(~key="pilates", ~name="Pilates", ~span=pilates, ~kind="Booking")] />
          <Calendar.Day day={date(17)} items=[item(~key="supplier-call", ~name="Supplier call", ~span=supplierCall, ~kind="Call")] />
          <Calendar.Day day={date(18)} empty={<p> {React.string("Nothing booked")} </p>} />
          <Calendar.Day day={date(19)} items=[item(~key="dance-rehearsal", ~name="Dance rehearsal", ~span=danceRehearsal, ~kind="Booking")] />
          <Calendar.Day day={date(20)} empty={<p> {React.string("Nothing booked")} </p>} />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, stepping

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let reader = Specimen_Reader.reader

let day = (day): KitReact.Time.t => KitReact.Time.day(reader, {year: 2025, month: 4, day})

let span = (start, finish) =>
  KitReact.Time.interval(reader, ~start=Date.fromString(start), ~end=Date.fromString(finish))

let hour = (hour): DeskGrid.hour => {time: KitReact.Time.clock(reader, {hour, minute: 0}), props: {}}

let head = (key, on): DeskGrid.head => {key, column: Day(day(on)), props: {}}

let placement = (row, span) => (
  {gridRow: `${Int.toString(row)} / span ${Int.toString(span)}`}: ReactDOM.Style.t
)

let column = (key): DeskGrid.column => {key, children: React.null, props: {}}

let nothing = <p> {React.string("Nothing booked")} </p>

let item = (~key, ~name, ~span, ~kind): Calendar.itemRow => {
  key,
  name,
  href: Some("../../../record/"),
  span,
  kind,
  standings: [],
  standingWords: [],
  room: None,
  state: None,
  props: {},
}

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-stepping"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-stepping-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Calendar controls"}}
      range={
        days: KitReact.Time.dayRange(reader, ~first={year: 2025, month: 4, day: 21}, ~last={year: 2025, month: 4, day: 25}),
        today: {label: "Today", props: {}},
        previous: {label: "Previous week", href: "?week=previous", props: {rel: "prev"}},
        next: {label: "Next week", href: "../stepped/", props: {rel: "next"}},
        date: {group: {id: "calendar-stepping-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader, value: Day(day(21)), today: day(14), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-stepping-scope-form-group",
          kind: Select,
          label: React.string("Hall"),
          labelProps: {htmlFor: "calendar-stepping-scope"},
          controlProps: {id: "calendar-stepping-scope"},
          options: [
            {label: React.string("All halls"), value: "all", props: {}},
            {label: React.string("Oakmere Hall"), value: "oakmere", props: {}},
          ],
        }),
        FormGroup({id: "calendar-stepping-room-form-group",
          kind: Select,
          label: React.string("Room"),
          labelProps: {htmlFor: "calendar-stepping-room"},
          controlProps: {id: "calendar-stepping-room"},
          options: [
            {label: React.string("All rooms"), value: "all", props: {}},
            {label: React.string("Main Hall"), value: "main-hall", props: {}},
          ],
        }),
      ]
      view={<KitReact.Roles.FormGroup id="calendar-stepping-view-form-group" label={React.string("View")} kind={KitReact.Roles.SegmentedControl.controlSet({name: "calendar-stepping-view", value: "week", options: Three({value: "week", content: React.string("Week")}, {value: "day", content: React.string("Day")}, {value: "month", content: React.string("Month")})})} />}
      actions={content: [PrimaryAction({children: React.string("New booking")})]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[
            head("2025-04-21", 21),
            head("2025-04-22", 22),
            head("2025-04-23", 23),
            head("2025-04-24", 24),
            head("2025-04-25", 25),
          ]
          hours=[
            hour(9),
            hour(10),
            hour(11),
            hour(12),
            hour(13),
          ]
          columns=[
            column("2025-04-21"),
            {
              key: "2025-04-22",
              children: <Calendar.Block
                name={React.string("Yoga class")}
                href="../../../record/"
                span={span("2025-04-22T09:00:00Z", "2025-04-22T10:00:00Z")}
                kind="Booking"
                props={{ariaLabel: "Yoga class, 10:00–11:00", style: placement(2, 1)}}
              />,
              props: {},
            },
            column("2025-04-23"),
            {
              key: "2025-04-24",
              children: <Calendar.Block
                name={React.string("Choir rehearsal")}
                href="../../../record/"
                span={span("2025-04-24T11:00:00Z", "2025-04-24T12:00:00Z")}
                kind="Booking"
                props={{ariaLabel: "Choir rehearsal, 12:00–13:00", style: placement(4, 1)}}
              />,
              props: {},
            },
            column("2025-04-25"),
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day day={day(21)} empty=nothing />
          <Calendar.Day
            day={day(22)}
            items=[
              item(
                ~key="bk-5201",
                ~name="Yoga class",
                ~span=span("2025-04-22T09:00:00Z", "2025-04-22T10:00:00Z"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day day={day(23)} empty=nothing />
          <Calendar.Day
            day={day(24)}
            items=[
              item(
                ~key="bk-5202",
                ~name="Choir rehearsal",
                ~span=span("2025-04-24T11:00:00Z", "2025-04-24T12:00:00Z"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day day={day(25)} empty=nothing />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, stepped

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let reader = Specimen_Reader.reader

let day = (month, day): KitReact.Time.t => KitReact.Time.day(reader, {year: 2025, month, day})

let span = (start, finish) =>
  KitReact.Time.interval(reader, ~start=Date.fromString(start), ~end=Date.fromString(finish))

let hour = (hour): DeskGrid.hour => {time: KitReact.Time.clock(reader, {hour, minute: 0}), props: {}}

let head = (key, month, on): DeskGrid.head => {key, column: Day(day(month, on)), props: {}}

let placement = (row, span) => (
  {gridRow: `${Int.toString(row)} / span ${Int.toString(span)}`}: ReactDOM.Style.t
)

let column = (key): DeskGrid.column => {key, children: React.null, props: {}}

let nothing = <p> {React.string("Nothing booked")} </p>

let item = (~key, ~name, ~span, ~kind): Calendar.itemRow => {
  key,
  name,
  href: Some("../../../record/"),
  span,
  kind,
  standings: [],
  standingWords: [],
  room: None,
  state: None,
  props: {},
}

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-stepped"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-stepped-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Calendar controls"}}
      range={
        days: KitReact.Time.dayRange(reader, ~first={year: 2025, month: 4, day: 28}, ~last={year: 2025, month: 5, day: 2}),
        today: {label: "Today", props: {}},
        previous: {label: "Previous week", href: "../stepping/", props: {rel: "prev"}},
        next: {label: "Next week", href: "?week=next", props: {rel: "next"}},
        date: {group: {id: "calendar-stepped-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader, value: Day(day(4, 28)), today: day(4, 14), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-stepped-scope-form-group",
          kind: Select,
          label: React.string("Hall"),
          labelProps: {htmlFor: "calendar-stepped-scope"},
          controlProps: {id: "calendar-stepped-scope"},
          options: [
            {label: React.string("All halls"), value: "all", props: {}},
            {label: React.string("Oakmere Hall"), value: "oakmere", props: {}},
          ],
        }),
        FormGroup({id: "calendar-stepped-room-form-group",
          kind: Select,
          label: React.string("Room"),
          labelProps: {htmlFor: "calendar-stepped-room"},
          controlProps: {id: "calendar-stepped-room"},
          options: [
            {label: React.string("All rooms"), value: "all", props: {}},
            {label: React.string("Main Hall"), value: "main-hall", props: {}},
          ],
        }),
      ]
      view={<KitReact.Roles.FormGroup id="calendar-stepped-view-form-group" label={React.string("View")} kind={KitReact.Roles.SegmentedControl.controlSet({name: "calendar-stepped-view", value: "week", options: Three({value: "week", content: React.string("Week")}, {value: "day", content: React.string("Day")}, {value: "month", content: React.string("Month")})})} />}
      actions={content: [PrimaryAction({children: React.string("New booking")})]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[
            head("2025-04-28", 4, 28),
            head("2025-04-29", 4, 29),
            head("2025-04-30", 4, 30),
            head("2025-05-01", 5, 1),
            head("2025-05-02", 5, 2),
          ]
          hours=[
            hour(9),
            hour(10),
            hour(11),
            hour(12),
            hour(13),
          ]
          columns=[
            column("2025-04-28"),
            {
              key: "2025-04-29",
              children: <Calendar.Block
                name={React.string("Yoga class")}
                href="../../../record/"
                span={span("2025-04-29T09:00:00Z", "2025-04-29T10:00:00Z")}
                kind="Booking"
                props={{ariaLabel: "Yoga class, 10:00–11:00", style: placement(2, 1)}}
              />,
              props: {},
            },
            column("2025-04-30"),
            {
              key: "2025-05-01",
              children: <Calendar.Block
                name={React.string("Choir rehearsal")}
                href="../../../record/"
                span={span("2025-05-01T11:00:00Z", "2025-05-01T12:00:00Z")}
                kind="Booking"
                props={{ariaLabel: "Choir rehearsal, 12:00–13:00", style: placement(4, 1)}}
              />,
              props: {},
            },
            column("2025-05-02"),
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day day={day(4, 28)} empty=nothing />
          <Calendar.Day
            day={day(4, 29)}
            items=[
              item(
                ~key="bk-5301",
                ~name="Yoga class",
                ~span=span("2025-04-29T09:00:00Z", "2025-04-29T10:00:00Z"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day day={day(4, 30)} empty=nothing />
          <Calendar.Day
            day={day(5, 1)}
            items=[
              item(
                ~key="bk-5302",
                ~name="Choir rehearsal",
                ~span=span("2025-05-01T11:00:00Z", "2025-05-01T12:00:00Z"),
                ~kind="Booking",
              ),
            ]
          />
          <Calendar.Day day={day(5, 2)} empty=nothing />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, saved-behind

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let hour = (hour): DeskGrid.hour => {time: KitReact.Time.clock(Specimen_Reader.reader, {hour, minute: 0}), props: {}}

let day = day => KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day})

let span = (on, start, end) =>
  KitReact.Time.interval(
    Specimen_Reader.reader,
    ~start=Date.fromString(`2025-04-${Int.toString(on)}T${start}:00+01:00`),
    ~end=Date.fromString(`2025-04-${Int.toString(on)}T${end}:00+01:00`),
  )

let head = (on): DeskGrid.head => {key: KitReact.Time.datetime(day(on)), column: DeskGrid.Day(day(on)), props: {}}

let band = (name, on, standing): DeskGrid.band => {
  name: React.string(name),
  href: Some("../../../record/"),
  first: on,
  last: on,
  stack: 1,
  standing,
  props: {},
}

let placement = (row, span) => ({gridRow: `${Int.toString(row)} / span ${Int.toString(span)}`}: ReactDOM.Style.t)
let sized = (row, span, size) =>
  ReactDOM.Style.unsafeAddProp(placement(row, span), "--kit-calendar-block-size", size)
let offset = (row, span, size, by) =>
  ReactDOM.Style.unsafeAddProp(sized(row, span, size), "--kit-calendar-block-offset", by)

let words = text => [KitReact.Words.Text(text)]

let failed = text => KitReact.Roles.Row.Failed({words: words(text), tryAgain: {}})
let conflict = (text, label) => KitReact.Roles.Row.Conflict({words: words(text), label, action: {}})
let processing = text => KitReact.Roles.Row.Processing(words(text))
let unsaved = text => KitReact.Roles.Row.Unsaved(words(text))

let bandRow = (key, name, standing): Calendar.bandRow => {
  key,
  name: React.string(name),
  href: Some("../../../record/"),
  state: None,
  standing,
  props: {},
}

let item = (~key, ~name, ~span, ~kind, ~standings=[], ~standingWords=[], ~standing): Calendar.itemRow => {
  key,
  name,
  href: Some("../../../record/"),
  span,
  kind,
  standings,
  standingWords,
  standing,
  room: None,
  state: None,
  props: {},
}

let boardMeeting = failed("This meeting was not moved: the connection dropped before it saved.")
let yogaClass = conflict("Maya Patel moved this booking to 11:00 while you were moving it to 10:00.", "Move it to 10:00")
let choirRehearsal = processing("Moving this booking…")
let toddlerGroup = unsaved("This booking will move when you are back online.")
let hallHandover = failed("This booking was not moved: the Main Hall is booked then.")
let springFair = failed("The spring fair was not moved: the connection dropped before it saved.")
let openDay = conflict("Maya Patel moved the open day to Tuesday while you were moving it to Wednesday.", "Move it to Wednesday")
let craftMarket = processing("Moving the craft market…")
let trainingDay = unsaved("The training day will move when you are back online.")

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-saved-behind"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-saved-behind-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Calendar controls"}}
      range={
        days: KitReact.Time.dayRange(Specimen_Reader.reader, ~first={year: 2025, month: 4, day: 14}, ~last={year: 2025, month: 4, day: 18}),
        today: {label: "Today", props: {}},
        previous: {label: "Previous week", href: "?week=previous", props: {rel: "prev"}},
        next: {label: "Next week", href: "?week=next", props: {rel: "next"}},
        date: {group: {id: "calendar-saved-behind-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: Specimen_Reader.reader, value: Day(KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14})), today: KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14}), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-saved-behind-scope-form-group",
          kind: Select,
          label: React.string("Hall"),
          labelProps: {htmlFor: "calendar-saved-behind-scope"},
          controlProps: {id: "calendar-saved-behind-scope"},
          options: [
            {label: React.string("All halls"), value: "all", props: {}},
            {label: React.string("Oakmere Hall"), value: "oakmere", props: {}},
          ],
        }),
        FormGroup({id: "calendar-saved-behind-room-form-group",
          kind: Select,
          label: React.string("Room"),
          labelProps: {htmlFor: "calendar-saved-behind-room"},
          controlProps: {id: "calendar-saved-behind-room"},
          options: [
            {label: React.string("All rooms"), value: "all", props: {}},
            {label: React.string("Main Hall"), value: "main-hall", props: {}},
          ],
        }),
      ]
      view={<KitReact.Roles.FormGroup id="calendar-saved-behind-view-form-group" label={React.string("View")} kind={KitReact.Roles.SegmentedControl.controlSet({name: "calendar-saved-behind-view", value: "week", options: Three({value: "week", content: React.string("Week")}, {value: "day", content: React.string("Day")}, {value: "month", content: React.string("Month")})})} />}
      actions={content: [PrimaryAction({children: React.string("New booking")})]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[
            head(14),
            head(15),
            head(16),
            head(17),
            head(18),
          ]
          bands=[
            band("Spring fair", 1, springFair),
            band("Open day", 2, openDay),
            band("Craft market", 3, craftMarket),
            band("Staff training day", 4, trainingDay),
          ]
          hours=[
            hour(9),
            hour(10),
            hour(11),
            hour(12),
            hour(13),
          ]
          columns=[
            {
              key: "2025-04-14",
              children: <Calendar.Block
                name={React.string("Board meeting")}
                href="../../../record/"
                span={span(14, "09:00", "09:30")}
                kind="Meeting"
                standing=boardMeeting
                props={{ariaLabel: "Board meeting, 09:00–09:30, This meeting was not moved", style: sized(1, 1, "calc(var(--kit-hour-height) / 2)")}}
              />,
              props: {},
            },
            {
              key: "2025-04-15",
              children: <Calendar.Block
                name={React.string("Yoga class")}
                href="../../../record/"
                span={span(15, "11:00", "12:00")}
                kind="Booking"
                standing=yogaClass
                props={{ariaLabel: "Yoga class, 11:00–12:00, Maya Patel moved this booking", style: placement(3, 1)}}
              />,
              props: {},
            },
            {
              key: "2025-04-16",
              children: <Calendar.Block
                name={React.string("Choir rehearsal")}
                href="../../../record/"
                span={span(16, "10:00", "11:30")}
                kind="Booking"
                standing=choirRehearsal
                props={{ariaLabel: "Choir rehearsal, 10:00–11:30, Moving this booking", style: sized(2, 2, "calc(var(--kit-hour-height) * 1.5)")}}
              />,
              props: {},
            },
            {
              key: "2025-04-17",
              children: <Calendar.Block
                name={React.string("Toddler group")}
                href="../../../record/"
                span={span(17, "09:30", "11:00")}
                kind="Booking"
                standing=toddlerGroup
                props={{ariaLabel: "Toddler group, 09:30–11:00, This booking will move when you are back online", style: offset(1, 2, "calc(var(--kit-hour-height) * 1.5)", "calc(var(--kit-hour-height) / 2)")}}
              />,
              props: {},
            },
            {
              key: "2025-04-18",
              children: <Calendar.Block
                name={React.string("Hall handover")}
                href="../../../record/"
                span={span(18, "13:00", "14:00")}
                kind="Booking"
                standings=[Uncovered]
                standingWords=["Cover needed"]
                standing=hallHandover
                props={{ariaLabel: "Hall handover, 13:00–14:00, Cover needed, This booking was not moved", style: placement(5, 1)}}
              />,
              props: {},
            },
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day
            day={day(14)}
            bands=[bandRow("ev-301", "Spring fair", springFair)]
            items=[item(~key="bk-5901", ~name="Board meeting", ~span=span(14, "09:00", "09:30"), ~kind="Meeting", ~standing=boardMeeting)]
          />
          <Calendar.Day
            day={day(15)}
            bands=[bandRow("ev-302", "Open day", openDay)]
            items=[item(~key="bk-5902", ~name="Yoga class", ~span=span(15, "11:00", "12:00"), ~kind="Booking", ~standing=yogaClass)]
          />
          <Calendar.Day
            day={day(16)}
            bands=[bandRow("ev-303", "Craft market", craftMarket)]
            items=[item(~key="bk-5903", ~name="Choir rehearsal", ~span=span(16, "10:00", "11:30"), ~kind="Booking", ~standing=choirRehearsal)]
          />
          <Calendar.Day
            day={day(17)}
            bands=[bandRow("ev-304", "Staff training day", trainingDay)]
            items=[item(~key="bk-5904", ~name="Toddler group", ~span=span(17, "09:30", "11:00"), ~kind="Booking", ~standing=toddlerGroup)]
          />
          <Calendar.Day
            day={day(18)}
            items=[item(~key="bk-5905", ~name="Hall handover", ~span=span(18, "13:00", "14:00"), ~kind="Booking", ~standings=[Uncovered], ~standingWords=["Cover needed"], ~standing=hallHandover)]
          />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, kinds

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let reader = Specimen_Reader.reader

let day = (day): KitReact.Time.t => KitReact.Time.day(reader, {year: 2025, month: 4, day})

let span = (start, finish) =>
  KitReact.Time.interval(reader, ~start=Date.fromString(start), ~end=Date.fromString(finish))

let hour = (hour): DeskGrid.hour => {time: KitReact.Time.clock(reader, {hour, minute: 0}), props: {}}

let currentTime: DeskGrid.currentTime = {
  props: {
    style: ReactDOM.Style.unsafeAddProp(
      {},
      "--kit-calendar-now-offset",
      "calc(3.5 * var(--kit-hour-height))",
    ),
  },
}

let hours = [
  hour(9),
  hour(10),
  hour(11),
  hour(12),
  hour(13),
  hour(14),
]

let placement = (row, span) => (
  {gridRow: `${Int.toString(row)} / span ${Int.toString(span)}`}: ReactDOM.Style.t
)

let item = (~key, ~name, ~span, ~kind, ~room=?): Calendar.itemRow => {
  key,
  name,
  href: Some("../../../record/"),
  span,
  kind,
  standings: [],
  standingWords: [],
  room,
  state: None,
  props: {},
}

let view = id =>
  <KitReact.Roles.FormGroup
    id={id ++ "-form-group"}
    label={React.string("View")}
    kind={KitReact.Roles.SegmentedControl.controlSet({name: id, value: "day", options: Three({value: "day", content: React.string("Day")}, {value: "week", content: React.string("Week")}, {value: "month", content: React.string("Month")})})}
  />

let newClass =
  Calendar.SplitAction(KitReact.Roles.SplitAction.Primary.role({
    id: "calendar-kinds-new",
    label: "Other kinds",
    variants: [
      {
        items: [
          KitReact.Menu.Item({name: "New private lesson"}),
          KitReact.Menu.Item({name: "New rental"}),
        ],
      },
    ],
    children: React.string("New class"),
  }))

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-kinds"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-kinds-filters"
      filtersLabel="Filters"
      toolbarProps={{ariaLabel: "Studio calendar controls"}}
      range={
        days: day(14),
        today: {label: "Today", props: {}},
        previous: {label: "Previous day", href: "?day=previous", props: {rel: "prev"}},
        next: {label: "Next day", href: "?day=next", props: {rel: "next"}},
        date: {group: {id: "calendar-kinds-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: Specimen_Reader.reader, value: Day(KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14})), today: KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14}), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-kinds-scope-form-group",
          kind: Select,
          label: React.string("Studio"),
          labelProps: {htmlFor: "calendar-kinds-scope"},
          controlProps: {id: "calendar-kinds-scope"},
          options: [
            {label: React.string("Studio one"), value: "one", props: {}},
          ],
        }),
        FormGroup({id: "calendar-kinds-kind-form-group",
          kind: Select,
          label: React.string("Kind"),
          labelProps: {htmlFor: "calendar-kinds-kind"},
          controlProps: {id: "calendar-kinds-kind"},
          options: [
            {label: React.string("All kinds"), value: "all", props: {}},
            {label: React.string("Classes"), value: "class", props: {}},
            {label: React.string("Private lessons"), value: "private-lesson", props: {}},
            {label: React.string("Rentals"), value: "rental", props: {}},
          ],
        }),
      ]
      view={view("calendar-kinds-view")}
      actions={content: [newClass]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[{key: "2025-04-14", column: Day(day(14)), props: {}}]
          hours
          currentTime
          columns=[
            {
              key: "2025-04-14",
              children: <>
                <Calendar.Block
                  name={React.string("Beginners ballet")}
                  href="../../../record/"
                  span={span("2025-04-14T08:00:00Z", "2025-04-14T10:00:00Z")}
                  kind="Class"
                  props={{ariaLabel: "Beginners ballet, 09:00–11:00", style: placement(1, 2)}}
                />
                <Calendar.Block
                  name={React.string("Studio hire")}
                  href="../../../record/"
                  span={span("2025-04-14T12:00:00Z", "2025-04-14T14:00:00Z")}
                  kind="Rental"
                  props={{ariaLabel: "Studio hire, 13:00–15:00", style: placement(5, 2)}}
                />
              </>,
              props: {},
            },
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day
            day={day(14)}
            items=[
              item(
                ~key="bk-6401",
                ~name="Beginners ballet",
                ~span=span("2025-04-14T08:00:00Z", "2025-04-14T10:00:00Z"),
                ~kind="Class",
              ),
              item(
                ~key="bk-6402",
                ~name="Studio hire",
                ~span=span("2025-04-14T12:00:00Z", "2025-04-14T14:00:00Z"),
                ~kind="Rental",
              ),
            ]
          />
        </Calendar.Days>
      </>}
    />
  </>

Calendar, kinds-inert

Open the whole page

Source
module Calendar = KitReact.Layouts.Calendar
module DeskGrid = Calendar.DeskGrid

let reader = Specimen_Reader.reader

let day = (day): KitReact.Time.t => KitReact.Time.day(reader, {year: 2025, month: 4, day})

let span = (start, finish) =>
  KitReact.Time.interval(reader, ~start=Date.fromString(start), ~end=Date.fromString(finish))

let hour = (hour): DeskGrid.hour => {time: KitReact.Time.clock(reader, {hour, minute: 0}), props: {}}

let currentTime: DeskGrid.currentTime = {
  props: {
    style: ReactDOM.Style.unsafeAddProp(
      {},
      "--kit-calendar-now-offset",
      "calc(3.5 * var(--kit-hour-height))",
    ),
  },
}

let hours = [
  hour(9),
  hour(10),
  hour(11),
  hour(12),
  hour(13),
  hour(14),
]

let placement = (row, span) => (
  {gridRow: `${Int.toString(row)} / span ${Int.toString(span)}`}: ReactDOM.Style.t
)

let item = (~key, ~name, ~span, ~kind, ~room=?, ~actions=?, ~standing=?): Calendar.itemRow => {
  key,
  name,
  href: Some("../../../record/"),
  span,
  kind,
  standings: [],
  standingWords: [],
  room,
  state: None,
  ?actions,
  ?standing,
  props: {},
}

let view = id =>
  <KitReact.Roles.FormGroup
    id={id ++ "-form-group"}
    label={React.string("View")}
    kind={KitReact.Roles.SegmentedControl.controlSet({name: id, value: "day", options: Three({value: "day", content: React.string("Day")}, {value: "week", content: React.string("Week")}, {value: "month", content: React.string("Month")})})}
  />

let newClass =
  Calendar.SplitAction(KitReact.Roles.SplitAction.Primary.role({
    id: "calendar-kinds-inert-new",
    label: "Other kinds",
    variants: [
      {
        items: [
          KitReact.Menu.Item({name: "New private lesson"}),
          KitReact.Menu.Item({name: "New rental"}),
        ],
      },
    ],
    state: Inert({id: "calendar-kinds-inert-new-main", words: [KitReact.Words.Text("Adding waits until this term's rooms are set.")]}),
    children: React.string("New class"),
  }))

@react.component
let make = (~owner: string, ~print: Specimen_Print.t) =>
  <>
    <a href="../../layout-calendar/#layout-calendar-kinds-inert"> {React.string(owner)} </a>
    <KitReact.Layouts.Calendar
      printIdentity={print.identity} printSubject={print.subject} printStanding={print.standing} printTenant=?print.tenant
      density=KitReact.Density.Desk
      subjectState=None
      scopeId="kit-calendar-kinds-inert-filters"
      filtersLabel="Filters"
      filtersControlState={Inert({id: "calendar-kinds-inert-filters-control", words: [KitReact.Words.Text("Filters wait until the studios are set.")]})}
      toolbarProps={{ariaLabel: "Studio calendar controls"}}
      range={
        days: day(14),
        today: {label: "Today", state: Inert({id: "calendar-kinds-inert-today", words: [KitReact.Words.Text("Today is outside this term.")]}), props: {}},
        previous: {label: "Previous day", href: "?day=previous", inert: true, props: {rel: "prev"}},
        next: {label: "Next day", href: "?day=next", inert: true, props: {rel: "next"}},
        date: {group: {id: "calendar-kinds-inert-date", label: React.string("Go to a date"), kind: KitReact.Roles.DateField.control({name: "date", reader: Specimen_Reader.reader, value: Day(KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14})), today: KitReact.Time.day(Specimen_Reader.reader, {year: 2025, month: 4, day: 14}), direction: Either})}},
      }
      scope=[
        FormGroup({id: "calendar-kinds-inert-scope-form-group",
          kind: Select,
          label: React.string("Studio"),
          labelProps: {htmlFor: "calendar-kinds-inert-scope"},
          controlProps: {id: "calendar-kinds-inert-scope"},
          options: [
            {label: React.string("Studio one"), value: "one", props: {}},
          ],
        }),
        FormGroup({id: "calendar-kinds-inert-kind-form-group",
          kind: Select,
          label: React.string("Kind"),
          labelProps: {htmlFor: "calendar-kinds-inert-kind"},
          controlProps: {id: "calendar-kinds-inert-kind"},
          options: [
            {label: React.string("All kinds"), value: "all", props: {}},
            {label: React.string("Classes"), value: "class", props: {}},
            {label: React.string("Private lessons"), value: "private-lesson", props: {}},
            {label: React.string("Rentals"), value: "rental", props: {}},
          ],
        }),
      ]
      view={view("calendar-kinds-inert-view")}
      actions={content: [newClass]}
      content={subjectRegion => <>
        <DeskGrid subjectRegion
          columnHeads=[{key: "2025-04-14", column: Day(day(14)), props: {}}]
          hours
          currentTime
          columns=[
            {
              key: "2025-04-14",
              children: <>
                <Calendar.Block
                  name={React.string("Beginners ballet")}
                  href="../../../record/"
                  span={span("2025-04-14T08:00:00Z", "2025-04-14T10:00:00Z")}
                  kind="Class"
                  props={{ariaLabel: "Beginners ballet, 09:00–11:00", style: placement(1, 2)}}
                />
                <Calendar.Block
                  name={React.string("Studio hire")}
                  href="../../../record/"
                  span={span("2025-04-14T12:00:00Z", "2025-04-14T14:00:00Z")}
                  kind="Rental"
                  props={{ariaLabel: "Studio hire, 13:00–15:00", style: placement(5, 2)}}
                />
              </>,
              props: {},
            },
          ]
        />
        <Calendar.Days subjectRegion>
          <Calendar.Day
            day={day(14)}
            items=[
              item(
                ~key="bk-6401",
                ~name="Beginners ballet",
                ~span=span("2025-04-14T08:00:00Z", "2025-04-14T10:00:00Z"),
                ~kind="Class",
                ~actions={content: [SubtleAction({children: React.string("Open this class"), state: KitReact.ActionState.Inert({id: "calendar-kinds-inert-open", words: [KitReact.Words.Text("Open this class once you are back online.")]})})]},
              ),
              item(
                ~key="bk-6402",
                ~name="Studio hire",
                ~span=span("2025-04-14T12:00:00Z", "2025-04-14T14:00:00Z"),
                ~kind="Rental",
                ~standing=Failed({words: [KitReact.Words.Text("This rental was not reinstated: the connection dropped before it saved.")], tryAgain: {}, tryAgainState: Inert({id: "calendar-kinds-inert-try-again", words: [KitReact.Words.Text("Try again once you are back online.")]})}),
              ),
            ]
          />
        </Calendar.Days>
      </>}
    />
  </>