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>