GrConfirmDialog
Берут, когда действие необратимо.
Обзор
Когда брать
- действие необратимо — удаление, отзыв доступа, отмена заказа: пауза перед ним и есть смысл компонента;
- подтверждение идёт на сервер —
confirmLoadingдержит окно открытым на время запроса,errorпоказывает отказ; - кнопка должна выглядеть опасной —
confirmToneкрасит подтверждение в тон последствия; - фокус ставится на отмену —
focusActionне даёт подтвердить опасное действие вслепую поEnter.
Когда взять другое
| Нужно | Берите |
|---|---|
| Запросить значение, а не согласие | GrPromptDialog |
| Вызвать подтверждение из кода | GrDialogService |
| Окно с произвольным содержимым | GrDialog |
| Подтверждение прямо у кнопки, без окна | GrPopover |
| Сообщить о результате, а не спросить | GrToaster |
Подтверждать безобидные действия не нужно: диалог, который всегда отвечают «да», перестаёт читаться и не остановит в тот единственный раз, когда это было важно. Обратимое действие лучше закрывает отмена в тосте.
Playground контролы выводятся из API, а не пишутся руками
Окно откроется поверх страницы с пропами, заданными справа.
0 изменено· состояние — в адресе
<GrConfirmDialog
title="Delete the report?"
description="The report and its 12 scheduled runs are removed. This cannot be undone."
confirmText="Delete report"
confirmTone="danger"
v-model="value"
/>Примеры 3 у каждого — живое демо и его код
Отзыв доступа участника
Ответ назван действием — «Revoke Grace», «Keep access», — потому что «OK» не говорит, на что соглашается, а
confirm-tone="danger"красит его в тон последствия.focus-action="cancel"ставит фокус на безопасный ответ: Enter, нажатый сразу после открытия, не должен ничего отзывать.Workspace members GWGrace WuAccountantOHOmar HaddadSalesINInes NovakOwnerОтмена, которую сервер отклоняет
close-on-confirm="false"держит окно, пока идёт запрос,confirm-loadingпоказывает ожидание в кнопке, аpersistentна это время выключает Esc и клик по фону — крестик и «Keep plan» работают. Первая попытка отклонена:errorставит ответ сервера над кнопками, там, где принимается решение.Business planRenews on 1 November · €240 a month
API порождается из пакета · разойтись не может
Props
| Prop | Type | по умолчанию | Описание |
|---|---|---|---|
title | string | undefined | undefined | Заголовок окна, он же его доступное имя. Не задан — берётся из локали. |
size | "sm" | "md" | "lg" | "xl" | "full" | undefined | undefined | Ширина окна по шкале `GrDialog`: от `sm` до `xl`, `full` — во весь экран. По умолчанию `md`. |
description | string | undefined | undefined | Текст вопроса в теле окна. Слот `default` его заменяет. |
closeOnBackdrop | boolean | undefined | true | Закрывать по клику на подложку. По умолчанию `true`; при `persistent` на время `confirmLoading` выключается. |
closeOnEsc | boolean | undefined | true | Закрывать по Esc. По умолчанию `true`; при `persistent` на время `confirmLoading` выключается. |
showHeader | boolean | undefined | true | Показывать шапку с заголовком и кнопкой закрытия. По умолчанию `true`. |
showCloseButton | boolean | undefined | true | Кнопка закрытия в шапке. По умолчанию `true`; при `persistent` тоже остаётся. |
headerConfig | GrDialogSectionConfig | undefined | undefined | Поля и линия шапки: `paddingX`, `paddingY`, `bordered`. |
footerConfig | GrDialogSectionConfig | undefined | undefined | Поля и линия подвала с кнопками. |
bodyConfig | GrDialogSectionConfig | undefined | undefined | Поля тела окна. `bordered` у тела не применяется. |
closeLabel | string | undefined | undefined | A11y-лейбл кнопки закрытия (i18n). |
buttonSize | "xs" | "sm" | "md" | "lg" | undefined | undefined | Размер обеих кнопок подвала. Не задан — кнопки берут размер из `GrConfigProvider`. |
confirmText | string | undefined | undefined | Подпись кнопки подтверждения. Не задана — берётся из локали. |
cancelText | string | undefined | undefined | Подпись кнопки отмены. Не задана — берётся из локали. |
confirmVariant | GrButtonVariant | undefined | "primary" | Вес кнопки подтверждения. По умолчанию `primary`. |
confirmTone |
| "primary" | Тон кнопки подтверждения: `danger` — для необратимого действия. По умолчанию `primary`. |
error | ResponseErrorInfo | null | undefined | null | Структура ошибки ответа сервера для показа в теле диалога (через `GrResponseErrorBanner`). Используется императивным `useDialogService` для async-`onConfirm`. `null` — блок скрыт. |
confirmLoading | boolean | undefined | false | Состояние загрузки кнопки Confirm (async-`onConfirm` in-flight). |
confirmDisabled | boolean | undefined | false | Принудительно дизейблит кнопку Confirm. |
closeOnConfirm | boolean | undefined | true | Закрывать ли диалог автоматически по клику Confirm. По умолчанию `true` (историческое поведение). `false` — отдаёт управление закрытием наружу (нужно `useDialogService`, который ждёт результат async-`onConfirm`). |
focusAction | GrConfirmDialogFocusAction | undefined | "cancel" | Какое действие получает фокус при открытии. По умолчанию «Отмена»: подтверждение бывает деструктивным, и `Enter` сразу после открытия не должен его запускать. `none` оставляет фокус на панели окна. Имя не `initialFocus` намеренно: у `GrModal`/`GrDrawer` так называется проп с элементом, а здесь выбирается действие. |
persistent | boolean | undefined | false | Запрет закрытия «мягкими» способами (Esc, клик по бэкдропу), пока идёт подтверждение (`confirmLoading`). Кнопка закрытия и «Отмена» остаются: окно без единого выхода — ловушка. |
modelValueобязательныйv-model | boolean | — | Открыто ли окно (`v-model`). Отмена закрывает его всегда, подтверждение — при `closeOnConfirm`. |
Слоты
| Slot | Type | Описание |
|---|---|---|
default | any | Содержимое диалога вместо текста из пропа `description`. |
error | { error: ResponseErrorInfo | null; } | Разбор ошибки вместо встроенного баннера. |
footer | any | Кнопки диалога вместо пары «отмена и подтверждение». |
События
| Event | Сигнатура | Описание |
|---|---|---|
update:modelValue | (event: "update:modelValue", value: boolean): void | — |
confirm | (event: "confirm"): void | — |
cancel | (event: "cancel"): void | — |
Типы
Все экспортируемые типы подпути, по файлам, где они объявлены. Имена, дженерики и тела читаются из файлов деклараций пакета и разойтись не могут.
import type { GrConfirmDialogFocusAction, GrConfirmDialogProps, GrConfirmDialogEmits } from '@feugene/granularity/components/GrConfirmDialog'Файлdist/types/components/GrConfirmDialog/GrConfirmDialog.vue.d.ts
Какое действие получает фокус при открытии окна.
type GrConfirmDialogFocusAction = 'confirm' | 'cancel' | 'none';interface GrConfirmDialogProps {
/**
* Открыто ли окно (`v-model`). Отмена закрывает его всегда, подтверждение — при
* `closeOnConfirm`.
*/
modelValue: boolean;
/** Заголовок окна, он же его доступное имя. Не задан — берётся из локали. */
title?: string;
/** Текст вопроса в теле окна. Слот `default` его заменяет. */
description?: string;
/**
* Закрывать по клику на подложку. По умолчанию `true`; при `persistent` на время
* `confirmLoading` выключается.
*/
closeOnBackdrop?: boolean;
/**
* Закрывать по Esc. По умолчанию `true`; при `persistent` на время `confirmLoading`
* выключается.
*/
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;
/** Вес кнопки подтверждения. По умолчанию `primary`. */
confirmVariant?: GrButtonVariant;
/** Тон кнопки подтверждения: `danger` — для необратимого действия. По умолчанию `primary`. */
confirmTone?: GrButtonTone;
/**
* Структура ошибки ответа сервера для показа в теле диалога
* (через `GrResponseErrorBanner`). Используется императивным
* `useDialogService` для async-`onConfirm`. `null` — блок скрыт.
*/
error?: ResponseErrorInfo | null;
/** Состояние загрузки кнопки Confirm (async-`onConfirm` in-flight). */
confirmLoading?: boolean;
/** Принудительно дизейблит кнопку Confirm. */
confirmDisabled?: boolean;
/**
* Закрывать ли диалог автоматически по клику Confirm. По умолчанию `true`
* (историческое поведение). `false` — отдаёт управление закрытием наружу
* (нужно `useDialogService`, который ждёт результат async-`onConfirm`).
*/
closeOnConfirm?: boolean;
/**
* Какое действие получает фокус при открытии. По умолчанию «Отмена»:
* подтверждение бывает деструктивным, и `Enter` сразу после открытия не
* должен его запускать. `none` оставляет фокус на панели окна.
*
* Имя не `initialFocus` намеренно: у `GrModal`/`GrDrawer` так называется
* проп с элементом, а здесь выбирается действие.
*/
focusAction?: GrConfirmDialogFocusAction;
/**
* Запрет закрытия «мягкими» способами (Esc, клик по бэкдропу), пока идёт
* подтверждение (`confirmLoading`). Кнопка закрытия и «Отмена» остаются:
* окно без единого выхода — ловушка.
*/
persistent?: boolean;
}interface GrConfirmDialogEmits {
/** Окно закрыто (`v-model`): кнопкой, крестиком, Esc или кликом по подложке. */
(e: 'update:modelValue', value: boolean): void;
/** Нажата кнопка подтверждения. Окно закрывается следом, если не выключен `closeOnConfirm`. */
(e: 'confirm'): void;
/** Нажата кнопка отмены. Крестик, Esc и подложка закрывают окно без этого события. */
(e: 'cancel'): void;
}Файлdist/types/components/GrConfirmDialog/config.d.ts
const grConfirmDialogConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrConfirmDialog">;Доступность
Клавиши
EscTab / Shift+Tab
| Клавиша | Действие |
|---|---|
| Esc | закрыть верхнее окно (если closeOnEsc) |
| Tab / Shift+Tab | цикл внутри фокус-ловушки |
Полный контракт пакета: docs/keyboard.md ↗
Использование из прозы библиотеки
Фокус при открытии
focusAction решает, какое действие получает фокус: 'cancel' (по умолчанию),
'confirm' или 'none' — фокус остаётся на панели окна.
Дефолт именно 'cancel', потому что окно подтверждения существует ради
рискованного действия: Enter, нажатый сразу после открытия, обязан отменять,
а не удалять. 'confirm' уместен там, где подтверждение — рутина
(«Сохранить перед выходом?»).
Фокус ставится со стороны содержимого после отрисовки, а не пропом
initialFocus у GrModal: элемент рождается внутри поддерева диалога, и
возврат его же пропом наверх замыкает рендер в цикл.
Деградация тихая. Переопределили слот #footer — своих кнопок компонент не
знает, фокус остаётся на панели; то же при focusAction="confirm" с
confirmDisabled. Это не ошибка конфигурации: свой футер значит, что фокусом
распоряжается потребитель.
Асинхронное подтверждение
closeOnConfirm: false отдаёт закрытие наружу: компонент шлёт confirm и
остаётся открытым, пока потребитель не снимет v-model. На время операции —
confirmLoading (кнопка показывает загрузку и не принимает клики) и error
(баннер ответа сервера в теле окна, слот #error — если баннера мало).
persistent на это время отключает мягкие способы закрытия — Esc и клик
по бэкдропу. Крестик и «Отмена» остаются: окно без единого выхода — ловушка, а
не защита. Тот же проп с той же семантикой есть у GrPromptDialog и GrDrawer.
<GrConfirmDialog
v-model="open"
confirm-tone="danger"
:confirm-loading="loading"
:error="error"
:close-on-confirm="false"
persistent
@confirm="submit"
/>useDialogService включает persistent всем своим окнам сам — там loading
означает, что запрос уже ушёл.
Пропы этого примера