GrPromptDialog

  • core
  • overlays
  • API · 40 props
  • ⌨ keys

Requests a short text input from the user in a dialog.

import { GrPromptDialog } from '@feugene/granularity/components/GrPromptDialog'

Overview

When to take it

  • a single value is needed — the name of a folder, the reason for a refusal, a comment on a return: setting up a screen for the sake of one field is expensive;
  • the value is checked — rules and required work before the confirmation, and fieldError shows the server’s refusal;
  • the text is long — multiline with autosize turns the field into a text area;
  • the call comes from code — useDialogService().prompt() returns a Promise with the value.

When to take something else

NeedTake
Ask for consent rather than for a valueGrConfirmDialog
There are several fieldsGrDialog + GrForm
The field lives on the screen itselfGrFormField + GrInput
Choose from ready optionsGrDialog + GrSelect

Playground controls come from the API, not from hand

Stage

The window opens above the page with the props set on the right.

0 changed· state is in the URL

Examples 3 each with a live demo and its code

  • Renaming a report from its row

    v-model:value puts the current name in the field before the window opens — a rename almost always starts from it — and v-model holds the window. confirm brings the new value, not just the fact that the user agreed.

    Revenue by planUpdated Today
    Churned accounts Q3Updated Yesterday
    Overdue invoicesUpdated 2 days ago
  • A reason for a return

    A reason does not fit one line: multiline turns the field into a textarea, autosize grows it with the answer, and maxlength with show-count names the limit before it is hit — a scrollbar inside three rows would hide what has been written.

    Order #4820 · Wool cardigan, navy, LDelivered on 2 October · returnable until 30 OctoberDelivered
  • A project name the server has to accept

    rules check the form of the name in the window, and field-error brings the server’s refusal under the field. With close-on-confirm="false" the window closes only when the project exists; confirm-loading and persistent keep it in place while the server answers. Try «q4-campaign».

    Projects · 2
    website-relaunchbilling-migration

API generated from the package · cannot drift

Props

PropTypedefaultDescription
titlestring | undefinedundefinedThe heading of the window, which is also its accessible name. Unset — taken from the locale.
requiredboolean | undefinedtrueAn empty value cannot be confirmed; the error shows after the first blur of the field or an attempt to confirm. `true` by default.
size"sm" | "md" | "lg" | "xl" | "full" | undefinedundefinedThe width of the window on the `GrDialog` scale: from `sm` to `xl`, `full` — the whole screen. `md` by default.
placeholderstring | undefinedundefinedHint in an empty field.
namestring | undefinedundefinedThe name of the field. In the set not for company’s sake: password managers rely on it too, and for some of them saving a value without a name simply does not work.
descriptionstring | undefinedundefinedAn explanation above the field. The `default` slot replaces it.
labelstring | undefinedundefinedThe label of the field, which is also its accessible name. Unset — taken from the locale.
closeOnBackdropboolean | undefinedtrueClose on a click on the backdrop. `true` by default; with `persistent` it is switched off while a confirmation or a check of the `rules` is in progress.
closeOnEscboolean | undefinedtrueClose on Esc. `true` by default; with `persistent` it is switched off while a confirmation or a check of the `rules` is in progress.
showHeaderboolean | undefinedtrueShow the header with the heading and the close button. `true` by default.
showCloseButtonboolean | undefinedtrueThe close button in the header. `true` by default; it stays with `persistent` too.
headerConfigGrDialogSectionConfig | undefinedundefinedThe paddings and the line of the header: `paddingX`, `paddingY`, `bordered`.
footerConfigGrDialogSectionConfig | undefinedundefinedThe paddings and the line of the footer with the buttons.
bodyConfigGrDialogSectionConfig | undefinedundefinedThe paddings of the window body. `bordered` does not apply to the body.

Slots

SlotTypeDescription
defaultanyThe content of the dialog instead of the text from the `description` prop.
error{ error: ResponseErrorInfo | null; }A breakdown of the error instead of the built-in banner.
footeranyThe buttons of the dialog instead of the "cancel and confirm" pair.

Events

EventSignatureDescription
update:modelValue(event: "update:modelValue", value: boolean): void—
confirm(event: "confirm", value: string): void—
cancel(event: "cancel"): void—
update:value(event: "update:value", value: string): void—

Types

Every exported type of the subpath, grouped by the file it lives in. Names, generics and bodies come from the package’s declaration files and cannot drift.

Import
import type { GrPromptDialogProps, GrPromptDialogEmits } from '@feugene/granularity/components/GrPromptDialog'

Filedist/types/components/GrPromptDialog/GrPromptDialog.vue.d.ts

GrPromptDialogPropsinterface
interface GrPromptDialogProps {
    modelValue: boolean;
    value: string;
    title?: string;
    description?: string;
    label?: string;
    placeholder?: string;
    closeOnBackdrop?: boolean;
    closeOnEsc?: boolean;
    showHeader?: boolean;
    showCloseButton?: boolean;
    size?: GrDialogSize;
    headerConfig?: GrDialogSectionConfig;
    footerConfig?: GrDialogSectionConfig;
    bodyConfig?: GrDialogSectionConfig;
    closeLabel?: string;
    buttonSize?: GrButtonSize;
    confirmText?: string;
    cancelText?: string;
    requiredErrorText?: string;
    confirmVariant?: GrButtonVariant;
    confirmTone?: GrButtonTone;
    required?: boolean;
    inputType?: GrInputType;
    inputmode?: InputHTMLAttributes['inputmode'];
    maxlength?: number;
    showCount?: boolean;
    autocomplete?: string;
    name?: string;
    autocapitalize?: 'off' | 'none' | 'on' | 'sentences' | 'words' | 'characters';
    spellcheck?: boolean;
    multiline?: boolean;
    rows?: number;
    autosize?: boolean;
    rules?: GrFormRule | GrFormRule[];
    error?: ResponseErrorInfo | null;
    fieldError?: string | null;
    confirmLoading?: boolean;
    confirmDisabled?: boolean;
    closeOnConfirm?: boolean;
    persistent?: boolean;
}
GrPromptDialogEmitsinterface
interface GrPromptDialogEmits {
    (e: 'update:modelValue', value: boolean): void;
    (e: 'update:value', value: string): void;
    (e: 'confirm', value: string): void;
    (e: 'cancel'): void;
}

Filedist/types/components/GrPromptDialog/config.d.ts

grPromptDialogConfigconst
const grPromptDialogConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrPromptDialog">;

Accessibility

Keys

EscTab / Shift+Tab

Usage from the library prose

The field

multiline switches GrInput to GrTextarea (rows, autosize) — the reason for a refusal or a comment must not be assembled through a slot. For a single-line field there are inputType (email, password, number, …) and inputmode. maxlength with showCount draws a counter; it is already linked to the field through aria-describedby.

The field is obliged to introduce itself to the browser

autocomplete, name, autocapitalize and spellcheck reach the field in both branches — the single-line and the multiline one. The default of all of them is the absence of the attribute: the window makes nothing up.

ts
dialog.prompt('Confirm the password', {
  inputType: 'password',
  autocomplete: 'current-password',
  name: 'password',
})

Without them a password manager does not recognise the field: 1Password, Safari and Chrome look at autocomplete and at the name of the field, and the confirmation of a password has to be typed by hand on every action.

autocomplete is not derived from inputType. One and the same password has three different correct values — current-password, new-password, one-time-code — and the window does not know which of them is right: it is used to ask for the old password, the new one and a one-time code alike. A guess here would be a lie.

autocapitalize and spellcheck are the pair to autocomplete="off" for secrets that are typed by hand. A 2FA backup code is compared byte for byte and is generated in mixed case: without autocapitalize="off" a mobile keyboard raises the first letter.

dialog.prompt has the same four fields — a divergence between the declarative and the imperative paths is guarded by the GrDialogService/__tests__/promptSurface.test.ts gate.

The field is not given an id: GrFormField generates it, and the field reads it from the context. A literal id used to break exactly what it was written for — two open dialogs (an ordinary one and one through the service) gave a duplicate DOM id, and <label for> led to someone else’s input.

Checking the value

required (true by default) is a quick synchronous “not empty” check. It blocks the button and does not go into the asynchronous engine.

Everything else is the rules prop with the same rules as in GrForm: type, min/max/len, pattern, a validator of your own, an asynchronous one included. There is no third special case of validation in the package — it is the same runFieldRules, and the messages are resolved by the same createGrFormMessageResolver (both are public).

vue
<GrPromptDialog
  v-model="open"
  v-model:value="email"
  :rules="{ type: 'email', message: 'A work address is required' }"
/>

The rules are run on blur (after the first touch) and on the confirmation. While a check is running, the button shows loading; the answer of a stale run is discarded — an asynchronous rule may come back after the value has already changed.

An error arrives from the outside as well: fieldError (the server-side validation of the field) is stronger than the built-in checks, and error draws a general banner in the body of the window.

The focus and the keyboard

On opening, the focus goes into the field rather than onto the panel of the window: the dialog exists precisely for the sake of the input. That is done on the side of the content rather than with the initialFocus prop of GrModal — the element is born inside the subtree of the dialog, and returning it upwards through a prop closes the render into a loop.

Enter in a single-line field confirms. In the multiline mode Enter remains a line break, and the confirmation is done with the button.