WebAwesome Laminar LogoWebAwesome Laminar

Dialog

Dialogs, sometimes called "modals", appear above the page and require the user's immediate attention.

val openEvent = EventBus[Boolean]()div(  Dialog(    _.open <-- openEvent,    _.label := "Dialog",    _.slots.footer(      Button(        _.variant.brand,        _.close.dialog      )("Close")    )  )("Lorem ipsum dolor sit amet, consectetur adipiscing elit."),  Button()(    onClick.mapTo(true) --> openEvent,    "Open Dialog"  ))

Examples

Dialog without Header

Headers are enabled by default. To render a dialog without a header, add the without-header attribute.

val openEvent = EventBus[Boolean]()div(  Dialog(    _.open <-- openEvent,    _.label         := "Dialog",    _.withoutHeader := true,    _.slots.footer(      Button(        _.variant.brand,        _.close.dialog      )("Close")    )  )("Lorem ipsum dolor sit amet, consectetur adipiscing elit."),  Button()(    onClick.mapTo(true) --> openEvent,    "Open Dialog"  ))

Footers can be used to display titles and more. Use the footer slot to add a footer to the dialog.

val openEvent = EventBus[Boolean]()div(  Dialog(    _.open <-- openEvent,    _.label := "Dialog",    _.slots.footer(      Button(        _.variant.brand,        _.close.dialog      )("Close")    )  )("Lorem ipsum dolor sit amet, consectetur adipiscing elit."),  Button()(    onClick.mapTo(true) --> openEvent,    "Open Dialog"  ))

Opening and Closing Dialogs Declaratively

You can open and close dialogs with JavaScript by toggling the open attribute, but you can also do it declaratively. Add the data-dialog="open id" to any button on the page, where id is the ID of the dialog you want to open.

Dialog(  _.id    := "dialog-opening",   _.label := "Dialog")("Lorem ipsum dolor sit amet, consectetur adipiscing elit."),Button(  _.open.dialog("dialog-opening") )("Open Dialog")

Similarly, you can add _.close.dialog to a button inside of a dialog to tell it to close.

Dialog(  _.id    := "dialog-dismiss",   _.label := "Dialog",  _.slots.footer(    Button(      _.variant.brand,      _.close.dialog     )("Close")  ))("Lorem ipsum dolor sit amet, consectetur adipiscing elit."),Button(  _.open.dialog("dialog-dismiss") )("Open Dialog")

Custom Width

Just use the --width custom property to set the dialog's width.

Dialog(    _.id    := "dialog-custom-width",    _.label := "Dialog",    _.style := "--width: 50vw;",     _.slots.footer(      Button(        _.variant.brand,        _.close.dialog      )("Close")    ))(  "Lorem ipsum dolor sit amet, consectetur adipiscing elit."),Button(  _.open.dialog("dialog-custom-width"))("Open Dialog")

Scrolling

By design, a dialog's height will never exceed that of the viewport. As such, dialogs will not scroll with the page ensuring the header and footer are always accessible to the user.

Dialog(  _.id    := "dialog-scrolling",  _.label := "Dialog",  _.slots.footer(    Button(      _.variant.brand,      _.close.dialog    )("Close")  ))(  div(    height.vh(150),    border  := "1px dashed var(--wa-color-surface-border)",    padding := "0 1rem",    p(      "Scroll down and give it a try! 👇"    )  )),Button(  _.open.dialog("dialog-scrolling"))("Open Dialog")

Header Actions

The header shows a functional close button by default. You can use the header-actions slot to add additional buttons if needed.

Dialog(  _.id    := "dialog-header-actions",  _.label := "Dialog",  _.slots.headerActions(    Button(      _.appearance.plain    )(      onClick --> Observer { _ =>        window.open(window.location.href)      },      Icon(        _.name  := "arrow-up-right-from-square",        _.label := "Open in new window"      )()    )  ),  _.slots.footer(    Button(      _.variant.brand,      _.close.dialog    )("Close")  ))(  "Lorem ipsum dolor sit amet, consectetur adipiscing elit."),Button(  _.open.dialog("dialog-header-actions"))("Open Dialog")

Light Dismissal

If you want the dialog to close when the user clicks on the overlay, add the light-dismiss attribute.

Dialog(  _.id           := "dialog-light-dismiss",  _.label        := "Dialog",  _.lightDismiss := true,   _.slots.footer(    Button(      _.variant.brand,      _.close.dialog    )("Close")  ))("Lorem ipsum dolor sit amet, consectetur adipiscing elit."),Button(  _.open.dialog("dialog-light-dismiss"))("Open Dialog")

Preventing the Dialog from Closing

By default, dialogs will close when the user clicks the close button, clicks the overlay, or presses the [[Escape]] key. In most cases, the default behavior is the best behavior in terms of UX. However, there are situations where this may be undesirable, such as when data loss will occur.

To keep the dialog open in such cases, you can cancel the wa-hide event. When canceled, the dialog will remain open and pulse briefly to draw the user's attention to it.

You can use event.detail.source to determine which element triggered the request to close. This example prevents the dialog from closing when the overlay is clicked, but allows the close button or [[Escape]] to dismiss it.

val closeDialogButton = Button(  _.variant.brand,  _.close.dialog)("Only this button will close it")div(  Dialog(    _.id    := "dialog-deny-close",    _.label := "Dialog",    _.onHide.map { event =>      if (event.detail.source != closeDialogButton.ref) {        event.preventDefault()      }    } --> Observer.empty,    _.slots.footer(      closeDialogButton    )  )("This dialog will only close when you click the button below."),  Button(    _.open.dialog("dialog-deny-close")  )("Open Dialog"))

Setting Initial Focus

To give focus to a specific element when the dialog opens, use the autofocus attribute.

Dialog(  _.id    := "dialog-focus",  _.label := "Dialog",  _.slots.footer(    Button(      _.variant.brand,      _.close.dialog    )("Close")  ))(  Input(    _.autofocus   := true,     _.placeholder := "I will have focus when the dialog is opened"  )()),Button(  _.open.dialog("dialog-focus"))("Open Dialog")