Skip to main content
On this pageFunctions

Ui/Dialog

Functions

close

functionsource
/** Programmatically closes the dialog. */
(model: Dialog.Model): UpdateReturn

descriptionId

functionsource
/**
 * 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

init

functionsource
/** Creates an initial dialog model from a config. Defaults to closed and non-animated. */
(config: InitConfig): Dialog.Model

open

functionsource
/** Programmatically opens the dialog. */
(model: Dialog.Model): UpdateReturn

titleId

functionsource
/**
 * 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

update

functionsource

Types

InitConfig

typesource
/**
 * 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
}>

RenderInfo

typesource
/**
 * 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>
}>

ViewInputs

typesource
/** Per-render view inputs passed to `view` via `h.submodel`'s `viewInputs` field. */
type ViewInputs = Readonly<{
  toView: (render: RenderInfo) => Html
}>

Constants

CloseDialog

constsource
/**
 * Calls `close()` on the native dialog element and unlocks page scroll when
 *  the close released the resources `ShowDialog` installed. A close that runs
 *  before the show has installed them leaves the lock alone. When the show
 *  then fails, it releases the lock itself. When the show succeeds, update
 *  closes the dialog again. If the dialog element is gone by the time the
 *  close runs, the Command calls `Dom.releaseDialogResources` instead. That
 *  releases the scroll lock, focus trap, return focus, and stack entry if the
 *  dialog still holds them.
 */
const CloseDialog: CommandDefinitionWithArgs<"CloseDialog", {
  id: String
}, Effect<{
  _tag: "CompletedCloseDialog"
}, never, never>>

Message

constsource
/** Union of all messages the dialog component can produce. */
const Message: MessageUnion<{
  CompletedCloseDialog: {}
  CompletedReleaseDialogResources: {}
  FailedShowDialog: {}
  GotAnimationMessage: {
    message: MessageUnion<{
      CompletedWaitForPaint: {}
      EndedAnimation: {}
      Hid: {}
      Showed: {}
    }>
  }
  RequestedClose: {}
  RequestedOpen: {}
  SucceededShowDialog: {}
  Unmounted: {}
}>

Model

constsource
/** 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>
}>

OutMessage

constsource
/** Union of out-messages the dialog component can produce. */
const OutMessage: MessageUnion<{
  Closed: {}
  Opened: {}
}>

ReleaseDialogResources

constsource
/**
 * 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>>

ShowDialog

constsource
/**
 * 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. If the dialog element is gone by the
 *  time the show runs, the lock is released and the Command reports
 *  `FailedShowDialog`. A closed dialog has no `OnUnmount`, so nothing else
 *  would release the lock. The update function then closes the Model. Without
 *  this close, the dialog would render open with no lock and no focus trap.
 *  The lock is also released if the Command is interrupted while it waits.
 */
const ShowDialog: CommandDefinitionWithArgs<"ShowDialog", {
  focusSelector: String
  id: String
}, Effect<{
  _tag: "SucceededShowDialog"
} | {
  _tag: "FailedShowDialog"
}, never, never>>

view

constsource
/**
 * 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: "SucceededShowDialog"
} | {
  _tag: "FailedShowDialog"
} | {
  _tag: "CompletedCloseDialog"
} | {
  _tag: "Unmounted"
} | {
  _tag: "CompletedReleaseDialogResources"
} | {
  _tag: "GotAnimationMessage"
  message: {
    _tag: "Showed"
  } | {
    _tag: "Hid"
  } | {
    _tag: "CompletedWaitForPaint"
  } | {
    _tag: "EndedAnimation"
  }
}, Readonly<{
  toView: (render: RenderInfo) => Html
}>>