Dialogs
Hosanna dialogs are ordinary Hosanna views hosted by the application dialog manager. Present one from a view with:
this.present(dialog, { isDialog: true });
Dialogs are placed on a separate navigation stack, focus is locked to that stack, and the previous application focus is restored when the stack empties.
Use the built-in dialog
Dialog provides a title, message, styled button list, and dismissal callback:
const dialog = Dialog({
id: 'deleteConfirmation',
title: 'Delete download?',
message: 'You can download it again later.',
buttonTexts: ['Delete', 'Cancel'],
buttonCallbacks: [
() => this.deleteDownload(),
() => {},
],
onDismissed: () => {
this.setFocusedSubview('downloadsButton');
},
});
this.present(dialog, { isDialog: true });
Each button callback runs first; the built-in dialog then calls dismiss().
onDismissed runs when the dialog has been removed from its aggregate view,
regardless of which button dismissed it.
Other built-in appearance fields include buttonStyles, buttonGap,
contentWidth, paddingX, paddingY, titleStyleKey,
messageStyleKey, backgroundColor, contentBackgroundColor, and
contentBackgroundUri.
Build a typed custom dialog
For a custom result, make the dialog view implement IDialogView<T>, store the
selected result, and call its callback from
onDidRemoveFromAggregateView():
export interface ConfirmDialogState extends ViewState {
onDismissed?: DialogDismissedWithValueCallback<boolean>;
}
@view('ConfirmDialog')
export class ConfirmDialogView
extends BaseView<ConfirmDialogState>
implements IDialogView<boolean> {
@state
onDismissed: DialogDismissedWithValueCallback<boolean> = () => {};
private accepted = false;
protected override getViews() {
return [
VGroup([
Label({ text: 'Continue?' }),
Button({ id: 'cancel', text: 'Cancel' })
.isInitialFocus()
.onClick(() => {
this.accepted = false;
this.dismiss();
}),
Button({ id: 'continue', text: 'Continue' })
.onClick(() => {
this.accepted = true;
this.dismiss();
}),
]),
];
}
override onDidRemoveFromAggregateView(owner: IAggregateView): void {
this.onDismissed(this.accepted);
}
}
The callbacks above are created inside the dialog class, so this.dismiss()
correctly refers to the dialog view. A free-standing DSL factory has no dialog
instance to use as this.
Dialog guidelines
- Give the first intended action initial focus and test directional focus inside the modal.
- Put outcome data in a typed
onDismissedcallback rather than reaching into the dialog after removal. - Restore focus in the presenting view when a specific control should regain focus.
- Use the keyboard dialog utility for the framework's keyboard flow; do not recreate its native/platform behavior as a generic dialog.