kit

Toast

Built revision v17.10.0

React
Topics

Human support

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

On Shell, Home

Toast

Show the toast

Yoga class moved to

Show the toast after Undo

Yoga class moved back to

Yoga class,

Source
type event
@get external disclosed: Dom.element => bool = "open"
@send external listen: (Dom.element, string, event => unit, ~capture: bool) => unit = "addEventListener"
@send external unlisten: (Dom.element, string, event => unit, ~capture: bool) => unit = "removeEventListener"
@val external document: Dom.element = "document"
@get external target: event => Dom.element = "target"

type step = Moved | Undone | Redone

let date = step =>
  switch step {
  | Undone => <KitReact.Roles.Time value={KitReact.Time.intervalWithDay(Specimen_Reader.reader, ~start=Date.fromString("2025-04-15T09:00:00Z"), ~end=Date.fromString("2025-04-15T11:00:00Z"))} />
  | Moved | Redone => <KitReact.Roles.Time value={KitReact.Time.intervalWithDay(Specimen_Reader.reader, ~start=Date.fromString("2025-04-17T09:00:00Z"), ~end=Date.fromString("2025-04-17T11:00:00Z"))} />
  }

let line = step =>
  switch step {
  | Moved | Redone => <> {React.string("Yoga class moved to ")} {date(step)} </>
  | Undone => <> {React.string("Yoga class moved back to ")} {date(step)} </>
  }

let next = step =>
  switch step {
  | Moved | Redone => Undone
  | Undone => Redone
  }

module AfterUndo = {
  @react.component
  let make = () => {
    let details = React.useRef(Nullable.null)
    let (shown, setShown) = React.useState(() => true)
    let (openings, setOpenings) = React.useState(() => 0)

    React.useEffect0(() =>
      switch Nullable.toOption(details.current) {
      | Some(element) =>
        let sync = () => setShown(_ => disclosed(element))
        let follow = event => {
          sync()
          if target(event) === element {
            setOpenings(count => count + 1)
          }
        }
        sync()
        listen(document, "toggle", follow, ~capture=true)
        Some(() => unlisten(document, "toggle", follow, ~capture=true))
      | None => None
      }
    )

    <details name="toast" ref={ReactDOM.Ref.domRef(details)}>
      <summary>{React.string("Show the toast after Undo")}</summary>
      {shown
        ? <KitReact.Roles.Toast
            key={Int.toString(openings)}
            confirmation=Some({
              id: "role-toast-undone",
              line: line(Undone),
              action: KitReact.Roles.Toast.Redo({props: {type_: "button"}}),
            })
          />
        : React.null}
    </details>
  }
}

@react.component
let make = () => {
  let details = React.useRef(Nullable.null)
  let (shown, setShown) = React.useState(() => true)
  let (step, setStep) = React.useState(() => Moved)
  let (serial, setSerial) = React.useState(() => 0)
  let (cleared, setCleared) = React.useState(() => false)

  React.useEffect0(() =>
    switch Nullable.toOption(details.current) {
    | Some(element) =>
      let sync = () => setShown(_ => disclosed(element))
      let follow = event => {
        sync()
        if target(event) === element && disclosed(element) {
          setCleared(_ => false)
          setSerial(count => count + 1)
        }
      }
      sync()
      listen(document, "toggle", follow, ~capture=true)
      Some(() => unlisten(document, "toggle", follow, ~capture=true))
    | None => None
    }
  )

  let id = serial == 0 ? "role-toast-move" : "role-toast-move-" ++ Int.toString(serial)
  let take = () => {
    setStep(next)
    setCleared(_ => false)
    setSerial(count => count + 1)
  }
  let given: KitReact.Roles.Toast.step = {props: {type_: "submit", form: "toast-step"}}

  let confirmation = shown && !cleared
    ? Some({
        KitReact.Roles.Toast.id,
        line: line(step),
        subjects: ["toast-subject"],
        action: step == Undone ? KitReact.Roles.Toast.Redo(given) : KitReact.Roles.Toast.Undo(given),
      })
    : None

  <>
    <details name="toast" ref={ReactDOM.Ref.domRef(details)}>
      <summary>{React.string("Show the toast")}</summary>
      <KitReact.Roles.Toast
        confirmation
        onWithdraw={withdrawal => if withdrawal.id == id { setCleared(_ => true) }}
      />
    </details>
    <AfterUndo />
    <form
      id="toast-step"
      method="get"
      action="#role-toast"
      onSubmit={event => {
        ReactEvent.Form.preventDefault(event)
        take()
      }}
    />
    <KitReact.Roles.SecondaryAction props={{id: "toast-replace", onClick: _ => take()}}>
      {React.string("Replace")}
    </KitReact.Roles.SecondaryAction>
    <KitReact.Roles.SecondaryAction props={{id: "toast-clear", onClick: _ => setCleared(_ => true)}}>
      {React.string("Clear")}
    </KitReact.Roles.SecondaryAction>
    <p id="toast-subject">
      {React.string("Yoga class, ")}
      {date(step)}
    </p>
  </>
}

Toast, confirmation

Show the toast

Invoice sent to Oakmere Yoga

Source
type event
@get external disclosed: Dom.element => bool = "open"
@send external listen: (Dom.element, string, event => unit, ~capture: bool) => unit = "addEventListener"
@send external unlisten: (Dom.element, string, event => unit, ~capture: bool) => unit = "removeEventListener"
@val external document: Dom.element = "document"
@get external target: event => Dom.element = "target"

@react.component
let make = () => {
  let details = React.useRef(Nullable.null)
  let (shown, setShown) = React.useState(() => true)
  let (openings, setOpenings) = React.useState(() => 0)

  React.useEffect0(() =>
    switch Nullable.toOption(details.current) {
    | Some(element) =>
      let sync = () => setShown(_ => disclosed(element))
      let follow = event => {
        sync()
        if target(event) === element {
          setOpenings(count => count + 1)
        }
      }
      sync()
      listen(document, "toggle", follow, ~capture=true)
      Some(() => unlisten(document, "toggle", follow, ~capture=true))
    | None => None
    }
  )

  <details name="toast" ref={ReactDOM.Ref.domRef(details)}>
    <summary>{React.string("Show the toast")}</summary>
    {shown
      ? <KitReact.Roles.Toast
          key={Int.toString(openings)}
          confirmation=Some({
            id: "role-toast-sent",
            line: React.string("Invoice sent to Oakmere Yoga"),
          })
        />
      : React.null}
  </details>
}

Toast, inert

Show the toast while the move saves

Yoga class moved to

Undo waits until the move has saved.

Close waits until the move has saved.

Show the toast while the undo saves

Yoga class moved back to

Redo waits until the undo has saved.

Close waits until the undo has saved.

Source
type event
@get external disclosed: Dom.element => bool = "open"
@send external listen: (Dom.element, string, event => unit, ~capture: bool) => unit = "addEventListener"
@send external unlisten: (Dom.element, string, event => unit, ~capture: bool) => unit = "removeEventListener"
@val external document: Dom.element = "document"
@get external target: event => Dom.element = "target"

module Case = {
  @react.component
  let make = (~summary, ~id, ~line, ~action, ~closeWords) => {
    let details = React.useRef(Nullable.null)
    let (shown, setShown) = React.useState(() => true)
    let (openings, setOpenings) = React.useState(() => 0)

    React.useEffect0(() =>
      switch Nullable.toOption(details.current) {
      | Some(element) =>
        let sync = () => setShown(_ => disclosed(element))
        let follow = event => {
          sync()
          if target(event) === element {
            setOpenings(count => count + 1)
          }
        }
        sync()
        listen(document, "toggle", follow, ~capture=true)
        Some(() => unlisten(document, "toggle", follow, ~capture=true))
      | None => None
      }
    )

    <details name="toast" ref={ReactDOM.Ref.domRef(details)}>
      <summary>{React.string(summary)}</summary>
      {shown
        ? <KitReact.Roles.Toast
            key={Int.toString(openings)}
            confirmation=Some({id, line, action})
            closeState={Inert({id: id ++ "-close", words: [KitReact.Words.Text(closeWords)]})}
          />
        : React.null}
    </details>
  }
}

@react.component
let make = () =>
  <div style={{paddingBlockEnd: "var(--kit-toast-height, 0px)"}}>
    <Case
      summary="Show the toast while the move saves"
      id="role-toast-inert-undo"
      closeWords="Close waits until the move has saved."
      line={<> {React.string("Yoga class moved to ")} <KitReact.Roles.Time value={KitReact.Time.intervalWithDay(Specimen_Reader.reader, ~start=Date.fromString("2025-04-17T09:00:00Z"), ~end=Date.fromString("2025-04-17T11:00:00Z"))} /> </>}
      action={KitReact.Roles.Toast.Undo({props: {type_: "button"}, state: Inert({id: "role-toast-inert-undo", words: [KitReact.Words.Text("Undo waits until the move has saved.")]})})}
    />
    <Case
      summary="Show the toast while the undo saves"
      id="role-toast-inert-redo"
      closeWords="Close waits until the undo has saved."
      line={<> {React.string("Yoga class moved back to ")} <KitReact.Roles.Time value={KitReact.Time.intervalWithDay(Specimen_Reader.reader, ~start=Date.fromString("2025-04-15T09:00:00Z"), ~end=Date.fromString("2025-04-15T11:00:00Z"))} /> </>}
      action={KitReact.Roles.Toast.Redo({props: {type_: "button"}, state: Inert({id: "role-toast-inert-redo", words: [KitReact.Words.Text("Redo waits until the undo has saved.")]})})}
    />
  </div>