GrPromptDialog

  • ядро
  • Слои
  • API · 40 props
  • ⌨ клавиши

Берут, когда нужно одно значение.

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

Обзор

Когда брать

  • нужно одно значение — имя папки, причина отказа, комментарий к возврату: заводить экран ради одного поля дорого;
  • значение проверяется — rules и required работают до подтверждения, fieldError показывает отказ сервера;
  • текст длинный — multiline с autosize превращает поле в область ввода;
  • вызов из кода — useDialogService().prompt() возвращает Promise со значением.

Когда взять другое

НужноБерите
Спросить согласие, а не значениеGrConfirmDialog
Полей несколькоGrDialog + GrForm
Поле живёт на самом экранеGrFormField + GrInput
Выбрать из готовых вариантовGrDialog + GrSelect

Playground контролы выводятся из API, а не пишутся руками

Сцена

Окно откроется поверх страницы с пропами, заданными справа.

0 изменено· состояние — в адресе

Примеры 3 у каждого — живое демо и его код

  • Переименование отчёта из его строки

    v-model:value кладёт текущее имя в поле до открытия окна — переименование почти всегда начинается с него, — а v-model держит окно. confirm приносит новое значение, а не только факт согласия.

    Revenue by planUpdated Today
    Churned accounts Q3Updated Yesterday
    Overdue invoicesUpdated 2 days ago
  • Причина возврата

    Причина не помещается в строку: multiline превращает поле в многострочное, autosize растит его вместе с ответом, а maxlength с show-count называет предел раньше, чем в него упрутся, — полоса прокрутки в трёх строках спрятала бы написанное.

    Order #4820 · Wool cardigan, navy, LDelivered on 2 October · returnable until 30 OctoberDelivered
  • Имя проекта, которое должен принять сервер

    rules проверяют вид имени в окне, а field-error ставит отказ сервера под поле. С close-on-confirm="false" окно закрывается, только когда проект создан; confirm-loading и persistent держат его на месте, пока отвечает сервер. Попробуйте «q4-campaign».

    Projects · 2
    website-relaunchbilling-migration

API порождается из пакета · разойтись не может

Props

PropTypeпо умолчаниюОписание
titlestring | undefinedundefinedЗаголовок окна, он же его доступное имя. Не задан — берётся из локали.
requiredboolean | undefinedtrueПустое значение не подтверждается; ошибка видна после первого ухода из поля или попытки подтвердить. По умолчанию `true`.
size"sm" | "md" | "lg" | "xl" | "full" | undefinedundefinedШирина окна по шкале `GrDialog`: от `sm` до `xl`, `full` — во весь экран. По умолчанию `md`.
placeholderstring | undefinedundefinedПодсказка в пустом поле.
namestring | undefinedundefinedИмя поля. В наборе не за компанию: менеджеры паролей опираются и на него, а сохранение значения без имени у части из них просто не срабатывает.
descriptionstring | undefinedundefinedПояснение над полем. Слот `default` его заменяет.
labelstring | undefinedundefinedПодпись поля, она же его доступное имя. Не задана — берётся из локали.
closeOnBackdropboolean | undefinedtrueЗакрывать по клику на подложку. По умолчанию `true`; при `persistent` на время подтверждения или проверки `rules` выключается.
closeOnEscboolean | undefinedtrueЗакрывать по Esc. По умолчанию `true`; при `persistent` на время подтверждения или проверки `rules` выключается.
showHeaderboolean | undefinedtrueПоказывать шапку с заголовком и кнопкой закрытия. По умолчанию `true`.
showCloseButtonboolean | undefinedtrueКнопка закрытия в шапке. По умолчанию `true`; при `persistent` тоже остаётся.
headerConfigGrDialogSectionConfig | undefinedundefinedПоля и линия шапки: `paddingX`, `paddingY`, `bordered`.
footerConfigGrDialogSectionConfig | undefinedundefinedПоля и линия подвала с кнопками.
bodyConfigGrDialogSectionConfig | undefinedundefinedПоля тела окна. `bordered` у тела не применяется.

Слоты

SlotTypeОписание
defaultanyСодержимое диалога вместо текста из пропа `description`.
error{ error: ResponseErrorInfo | null; }Разбор ошибки вместо встроенного баннера.
footeranyКнопки диалога вместо пары «отмена и подтверждение».

События

EventСигнатураОписание
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—

Типы

Все экспортируемые типы подпути, по файлам, где они объявлены. Имена, дженерики и тела читаются из файлов деклараций пакета и разойтись не могут.

Импорт
import type { GrPromptDialogProps, GrPromptDialogEmits } from '@feugene/granularity/components/GrPromptDialog'

Файлdist/types/components/GrPromptDialog/GrPromptDialog.vue.d.ts

GrPromptDialogPropsinterface
interface GrPromptDialogProps {
    /** Открыто ли окно (`v-model`). Отмена закрывает его всегда, подтверждение — при `closeOnConfirm`. */
    modelValue: boolean;
    /** Значение поля (`v-model:value`). Между открытиями окно его не сбрасывает. */
    value: string;
    /** Заголовок окна, он же его доступное имя. Не задан — берётся из локали. */
    title?: string;
    /** Пояснение над полем. Слот `default` его заменяет. */
    description?: string;
    /** Подпись поля, она же его доступное имя. Не задана — берётся из локали. */
    label?: string;
    /** Подсказка в пустом поле. */
    placeholder?: string;
    /**
     * Закрывать по клику на подложку. По умолчанию `true`; при `persistent` на время подтверждения
     * или проверки `rules` выключается.
     */
    closeOnBackdrop?: boolean;
    /**
     * Закрывать по Esc. По умолчанию `true`; при `persistent` на время подтверждения или проверки
     * `rules` выключается.
     */
    closeOnEsc?: boolean;
    /** Показывать шапку с заголовком и кнопкой закрытия. По умолчанию `true`. */
    showHeader?: boolean;
    /** Кнопка закрытия в шапке. По умолчанию `true`; при `persistent` тоже остаётся. */
    showCloseButton?: boolean;
    /** Ширина окна по шкале `GrDialog`: от `sm` до `xl`, `full` — во весь экран. По умолчанию `md`. */
    size?: GrDialogSize;
    /** Поля и линия шапки: `paddingX`, `paddingY`, `bordered`. */
    headerConfig?: GrDialogSectionConfig;
    /** Поля и линия подвала с кнопками. */
    footerConfig?: GrDialogSectionConfig;
    /** Поля тела окна. `bordered` у тела не применяется. */
    bodyConfig?: GrDialogSectionConfig;
    /** A11y-лейбл кнопки закрытия (i18n). */
    closeLabel?: string;
    /** Размер обеих кнопок подвала. Не задан — кнопки берут размер из `GrConfigProvider`. */
    buttonSize?: GrButtonSize;
    /** Подпись кнопки подтверждения. Не задана — берётся из локали. */
    confirmText?: string;
    /** Подпись кнопки отмены. Не задана — берётся из локали. */
    cancelText?: string;
    /** Текст ошибки для пустого значения при `required=true` (i18n). */
    requiredErrorText?: string;
    /** Вес кнопки подтверждения. По умолчанию `primary`. */
    confirmVariant?: GrButtonVariant;
    /** Тон кнопки подтверждения: `danger` — для необратимого действия. По умолчанию `primary`. */
    confirmTone?: GrButtonTone;
    /**
     * Пустое значение не подтверждается; ошибка видна после первого ухода из поля или попытки
     * подтвердить. По умолчанию `true`.
     */
    required?: boolean;
    /** Тип однострочного поля. В многострочном режиме не применяется. */
    inputType?: GrInputType;
    /** Программная клавиатура на мобильных. */
    inputmode?: InputHTMLAttributes['inputmode'];
    /** Ограничение длины; со `showCount` рисуется счётчик. */
    maxlength?: number;
    /** Счётчик символов под полем; с `maxlength` — в виде `len/maxlength`. */
    showCount?: boolean;
    /**
     * Что у человека спрашивают — для браузера и менеджера паролей
     * (`current-password`, `new-password`, `one-time-code`, `off`).
     *
     * Из `inputType` не выводится: у одного и того же `password` три разных
     * правильных значения, и окно не знает, спрашивают у него старый пароль,
     * новый или одноразовый код. Значение выбирает потребитель.
     */
    autocomplete?: string;
    /**
     * Имя поля. В наборе не за компанию: менеджеры паролей опираются и на него, а
     * сохранение значения без имени у части из них просто не срабатывает.
     */
    name?: string;
    /**
     * Автозаглавная у программной клавиатуры. Пара к `autocomplete="off"` для
     * секретов, которые вводят руками: резервный код 2FA сверяется байт в байт, а
     * клавиатура поднимает первую букву.
     */
    autocapitalize?: 'off' | 'none' | 'on' | 'sentences' | 'words' | 'characters';
    /** Проверка орфографии. Вторая половина той же пары. */
    spellcheck?: boolean;
    /** Многострочный ввод: вместо `GrInput` рисуется `GrTextarea`. */
    multiline?: boolean;
    /** Высота многострочного поля в строках. */
    rows?: number;
    /** Автоподбор высоты многострочного поля под содержимое. */
    autosize?: boolean;
    /**
     * Правила проверки значения — те же, что у `GrForm` (`required`, `type`,
     * `min`/`max`/`len`, `pattern`, свой в т.ч. асинхронный `validator`).
     * Прогоняются на blur (после первого касания) и на подтверждении.
     */
    rules?: GrFormRule | GrFormRule[];
    /**
     * Структура ошибки ответа сервера для показа общим блоком в теле диалога
     * (через `GrResponseErrorBanner`). `null` — блок скрыт.
     */
    error?: ResponseErrorInfo | null;
    /**
     * Внешняя ошибка поля ввода (например, серверная валидация). Имеет приоритет
     * над встроенной проверкой. `null`/`undefined` — нет внешней ошибки.
     */
    fieldError?: string | null;
    /** Состояние загрузки кнопки Confirm (async-`onConfirm` in-flight). */
    confirmLoading?: boolean;
    /** Принудительно дизейблит кнопку Confirm. */
    confirmDisabled?: boolean;
    /**
     * Закрывать ли диалог автоматически по клику Confirm. По умолчанию `true`.
     * `false` — отдаёт управление закрытием наружу (нужно `useDialogService`).
     */
    closeOnConfirm?: boolean;
    /**
     * Запрет закрытия «мягкими» способами (Esc, клик по бэкдропу), пока идёт
     * подтверждение или проверка `rules`. Кнопка закрытия и «Отмена» остаются:
     * окно без единого выхода — ловушка.
     */
    persistent?: boolean;
}
GrPromptDialogEmitsinterface
interface GrPromptDialogEmits {
    /** Окно закрыто (`v-model`): кнопкой, крестиком, Esc или кликом по подложке. */
    (e: 'update:modelValue', value: boolean): void;
    /** Значение поля изменилось при вводе (`v-model:value`). */
    (e: 'update:value', value: string): void;
    /**
     * Значение подтверждено — кнопкой или `Enter` в однострочном поле — и прошло `required` и
     * `rules`. Окно закрывается следом, если не выключен `closeOnConfirm`.
     */
    (e: 'confirm', value: string): void;
    /** Нажата кнопка отмены. Крестик, Esc и подложка закрывают окно без этого события. */
    (e: 'cancel'): void;
}

Файлdist/types/components/GrPromptDialog/config.d.ts

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

Доступность

Клавиши

EscTab / Shift+Tab

КлавишаДействие
Escзакрыть верхнее окно (если closeOnEsc)
Tab / Shift+Tabцикл внутри фокус-ловушки

Использование из прозы библиотеки

Поле

multiline переключает GrInput на GrTextarea (rows, autosize) — причина отказа или комментарий не должны собираться через слот. Для однострочного поля есть inputType (email, password, number, …) и inputmode. maxlength со showCount рисует счётчик; он уже связан с полем через aria-describedby.

Поле обязано представиться браузеру

autocomplete, name, autocapitalize и spellcheck доезжают до поля в обеих ветвях — и однострочной, и многострочной. Умолчание у всех — отсутствие атрибута: окно ничего не додумывает.

ts
dialog.prompt('Подтвердите пароль', {
  inputType: 'password',
  autocomplete: 'current-password',
  name: 'password',
})

Без них менеджер паролей поле не опознаёт: 1Password, Safari и Chrome смотрят на autocomplete и на имя поля, и подтверждение пароля приходится набирать руками на каждом действии.

autocomplete из inputType не выводится. У одного и того же password три разных правильных значения — current-password, new-password, one-time-code, — и окно не знает, которое из них верно: им спрашивают и старый пароль, и новый, и одноразовый код. Догадка здесь была бы враньём.

autocapitalize и spellcheck — пара к autocomplete="off" для секретов, которые вводят руками. Резервный код 2FA сверяется байт в байт, а генерируется в смешанном регистре: без autocapitalize="off" мобильная клавиатура поднимает первую букву.

Те же четыре поля есть у dialog.prompt — расхождение между декларативным и императивным путями сторожит гейт GrDialogService/__tests__/promptSurface.test.ts.

id полю не задаётся: его генерирует GrFormField, а поле читает из контекста. Литеральный id ломал ровно то, ради чего он был написан — два открытых диалога (обычный и через сервис) давали дубликат DOM-id, и <label for> уводил на чужой инпут.

Проверка значения

required (по умолчанию true) — быстрая синхронная проверка «непусто». Она блокирует кнопку и не ходит в асинхронный движок.

Всё остальное — проп rules с теми же правилами, что у GrForm: type, min/max/len, pattern, свой в том числе асинхронный validator. Третьего частного случая валидации в пакете нет — это тот же runFieldRules, и сообщения резолвит тот же createGrFormMessageResolver (оба публичны).

vue
<GrPromptDialog
  v-model="open"
  v-model:value="email"
  :rules="{ type: 'email', message: 'Нужен рабочий адрес' }"
/>

Правила прогоняются на blur (после первого касания) и на подтверждении. Пока проверка идёт, кнопка показывает загрузку; ответ устаревшего прогона отбрасывается — асинхронное правило может вернуться уже после того, как значение сменилось.

Ошибка приходит и снаружи: fieldError (серверная валидация поля) сильнее встроенных проверок, error рисует общий баннер в теле окна.

Фокус и клавиатура

При открытии фокус уходит в поле, а не на панель окна: диалог существует ровно ради ввода. Сделано на стороне содержимого, а не пропом initialFocus у GrModal, — элемент рождается внутри поддерева диалога, и возврат его же пропом наверх замыкает рендер в цикл.

Enter в однострочном поле подтверждает. В многострочном режиме Enter остаётся переводом строки, и подтверждают кнопкой.