On this pageFunctions
Ui/Dialog
/**
* Returns the framework-managed id the dialog's `aria-describedby` points at,
* the `-dialog-description` suffix on `model.id`.
*
* The primary path is spreading `RenderInfo`'s `description` onto your
* description element (`h.p([...description], [...])`), which carries this id
* for you. Reach for this helper only when you need the id as a value outside
* `toView`: a Command that calls `getElementById`, a cross-element
* `aria-describedby`, or a test. Do not hand-roll the id string.
*/
(model: Dialog.Model): string/** Creates an initial dialog model from a config. Defaults to closed and non-animated. */
(config: InitConfig): Dialog.Model/**
* Returns the framework-managed id the dialog's `aria-labelledby` points at,
* the `-dialog-title` suffix on `model.id`.
*
* The primary path is spreading `RenderInfo`'s `title` onto your heading
* (`h.h2([...title], [...])`), which carries this id for you. Reach for this
* helper only when you need the id as a value outside `toView`: a Command that
* calls `getElementById`, a cross-element `aria-describedby`, or a test. Do not
* hand-roll the id string.
*/
(model: Dialog.Model): string/** Processes a dialog message and returns the next model and commands. */
(
model: Dialog.Model,
message: {
_tag: "RequestedOpen"
} | {
_tag: "RequestedClose"
} | {
_tag: "CompletedShowDialog"
} | {
_tag: "CompletedCloseDialog"
} | {
_tag: "Unmounted"
} | {
_tag: "CompletedReleaseDialogResources"
} | {
_tag: "GotAnimationMessage"
message: {
_tag: "Showed"
} | {
_tag: "Hid"
} | {
_tag: "CompletedWaitForPaint"
} | {
_tag: "EndedAnimation"
}
}
): UpdateReturn/**
* Configuration for creating a dialog model with `init`. The `id` must be
* non-empty and unique within the document: it keys the dialog element, its
* ARIA references, and the framework's per-dialog resource cleanup, so a
* duplicate or empty id breaks cleanup accounting.
*
* The dialog derives framework-managed ids from this `id`: `-dialog-title`,
* `-dialog-description`, and `-panel` (the animation panel). Spread
* `RenderInfo`'s `title` / `description` onto your heading and description
* elements rather than constructing those ids yourself.
*/
type InitConfig = Readonly<{
focusSelector: string
id: string
isAnimated: boolean
isOpen: boolean
}>/**
* Render-time payload published to the consumer's `toView`.
*
* - `dialog`: attributes for the native `<dialog>` element. Carries
* the id, ARIA labelling, `open` prop, positioning style, the
* `OnCancel` handler that wires Escape to `RequestedClose`, and an
* `OnUnmount` backstop that releases framework hygiene (scroll lock,
* focus trap, return focus) if the element is removed from the DOM
* while still open, such as navigating away from a route-keyed subtree.
* The consumer MUST render an `h.dialog(...)` element so the framework
* can open and close it, and so the unmount backstop can fire.
* - `backdrop`: attributes for the backdrop element. Includes the
* Animation data attributes and the `OnClick` handler that closes
* the dialog on outside-click (suppressed while a leave animation
* is in progress).
* - `panel`: attributes for the panel element. Includes the panel id
* (`${model.id}-panel`) and the Animation data attributes.
* - `title`: attributes for the accessible-name heading. Carries the
* framework-managed id the dialog's `aria-labelledby` points at. Spread
* onto your heading element (`h.h2([...title], [...])`) so labelling
* wires up without hand-rolling the id.
* - `description`: attributes for the description element. Carries the
* framework-managed id the dialog's `aria-describedby` points at. Spread
* onto your description element (`h.p([...description], [...])`).
* - `initialFocus`: attributes for the element that should receive focus when
* the dialog opens. Spread onto that element (`h.input([...initialFocus])`).
* A configured `focusSelector` (see `init`) takes precedence, and focus
* falls back to the default when no element carries the group.
* - `closeButton`: attributes for an in-panel close control such as a Cancel
* or dismiss button. Carries the `OnClick` handler that closes the
* dialog (suppressed while a leave animation is in progress). Spread
* onto your own button so a plain close needs no parent message. Sets
* `type="button"` so that a close control inside a `form` element in the
* panel closes without also submitting the form. Spread a later `h.Type`
* to override it.
* - `isVisible`: derived from `isOpen` and the Animation
* `transitionState`. The consumer renders backdrop + panel only
* while this is true.
*/
type RenderInfo = Readonly<{
backdrop: ReadonlyArray<ChildAttribute>
closeButton: ReadonlyArray<ChildAttribute>
description: ReadonlyArray<ChildAttribute>
dialog: ReadonlyArray<ChildAttribute>
initialFocus: ReadonlyArray<ChildAttribute>
isVisible: boolean
panel: ReadonlyArray<ChildAttribute>
title: ReadonlyArray<ChildAttribute>
}>/** Per-render view inputs passed to `view` via `h.submodel`'s `viewInputs` field. */
type ViewInputs = Readonly<{
toView: (render: RenderInfo) => Html
}>/** Calls `close()` on the native dialog element and unlocks page scroll. */
const CloseDialog: CommandDefinitionWithArgs<"CloseDialog", {
id: String
}, Effect<{
_tag: "CompletedCloseDialog"
}, never, never>>/**
* Sent once the dialog has transitioned to closed. Programmatic
* `Dialog.close` on an already-closed model is a no-op that does not
* re-emit; calling close while a leave animation is in progress is
* also a no-op.
*/
const Closed: CallableTaggedStruct<"Closed", {}>/** Sent when the close-dialog command completes. */
const CompletedCloseDialog: CallableTaggedStruct<"CompletedCloseDialog", {}>/** Sent when the release-dialog-resources command completes. */
const CompletedReleaseDialogResources: CallableTaggedStruct<"CompletedReleaseDialogResources", {}>/** Sent when the show-dialog command completes. */
const CompletedShowDialog: CallableTaggedStruct<"CompletedShowDialog", {}>/** Wraps an Animation submodel message for delegation. */
const GotAnimationMessage: CallableTaggedStruct<"GotAnimationMessage", {
message: Union<[CallableTaggedStruct<"Showed", {}>, CallableTaggedStruct<"Hid", {}>, CallableTaggedStruct<"CompletedWaitForPaint", {}>, CallableTaggedStruct<"EndedAnimation", {}>]>
}>/** Union of all messages the dialog component can produce. */
const Message: S.Union<[typeof RequestedOpen, typeof RequestedClose, typeof CompletedShowDialog, typeof CompletedCloseDialog, typeof Unmounted, typeof CompletedReleaseDialogResources, typeof GotAnimationMessage]>/** Schema for the dialog component's state, tracking its unique ID, open/closed status, animation support, and animation lifecycle phase. */
const Model: Struct<{
animation: Struct<{
id: String
isShowing: Boolean
transitionState: Literals<readonly ["Idle", "EnterStart", "EnterAnimating", "LeaveStart", "LeaveAnimating"]>
}>
id: String
isAnimated: Boolean
isOpen: Boolean
maybeFocusSelector: Option<String>
}>/**
* Sent once the dialog has transitioned to open. Fires after `update`
* has processed `RequestedOpen` and `isOpen` reflects the new state.
* Programmatic `Dialog.open` on an already-open model is a no-op that
* does not re-emit.
*/
const Opened: CallableTaggedStruct<"Opened", {}>/** Union of out-messages the dialog component can produce. */
const OutMessage: Union<readonly [CallableTaggedStruct<"Opened", {}>, CallableTaggedStruct<"Closed", {}>]>/**
* Releases the framework hygiene the dialog holds while open (scroll lock,
* focus trap, return focus, stack entry) when the element unmounts without a
* purposeful close. Idempotent: a no-op if the dialog already released its
* resources through `CloseDialog`.
*/
const ReleaseDialogResources: CommandDefinitionWithArgs<"ReleaseDialogResources", {
id: String
}, Effect<{
_tag: "CompletedReleaseDialogResources"
}, never, never>>/** Sent when the dialog should close (Escape key, backdrop click, or programmatic). */
const RequestedClose: CallableTaggedStruct<"RequestedClose", {}>/** Sent when the dialog should open. Triggers the ShowDialog command. */
const RequestedOpen: CallableTaggedStruct<"RequestedOpen", {}>/**
* Locks page scroll and opens the native dialog element through
* `Dom.showDialog`, which calls `show()` (not native `showModal()`) so other
* high-z-index overlays stay interactive. It layers the dialog with a high
* z-index, traps focus, and dispatches a `cancel` event on Esc. The Dialog
* component supplies its own backdrop.
*/
const ShowDialog: CommandDefinitionWithArgs<"ShowDialog", {
focusSelector: String
id: String
}, Effect<{
_tag: "CompletedShowDialog"
}, never, never>>/**
* Sent when the native `<dialog>` element is removed from the DOM, the classic
* case being navigation away from a route-keyed subtree that contains the
* dialog. When the dialog still holds framework resources, `update` triggers
* the hygiene-only `ReleaseDialogResources` command and resets the model to a
* clean closed state. Does not emit `Closed` or run any consumer close
* Commands: it is a backstop, not the purposeful close.
*/
const Unmounted: CallableTaggedStruct<"Unmounted", {}>/**
* Renders a headless dialog component backed by the native `<dialog>`
* element. `ShowDialog` opens it through `Dom.showDialog`, which uses `show()`
* (not native `showModal()`) with a high z-index, a focus trap, a
* component-supplied backdrop, and a `cancel` event dispatched on Esc.
*/
const view: SubmodelView<Dialog.Model, {
_tag: "RequestedOpen"
} | {
_tag: "RequestedClose"
} | {
_tag: "CompletedShowDialog"
} | {
_tag: "CompletedCloseDialog"
} | {
_tag: "Unmounted"
} | {
_tag: "CompletedReleaseDialogResources"
} | {
_tag: "GotAnimationMessage"
message: {
_tag: "Showed"
} | {
_tag: "Hid"
} | {
_tag: "CompletedWaitForPaint"
} | {
_tag: "EndedAnimation"
}
}, Readonly<{
toView: (render: RenderInfo) => Html
}>>