GrConfirmDialog

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

Берут, когда действие необратимо.

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

Обзор

Когда брать

  • действие необратимо — удаление, отзыв доступа, отмена заказа: пауза перед ним и есть смысл компонента;
  • подтверждение идёт на сервер — confirmLoading держит окно открытым на время запроса, error показывает отказ;
  • кнопка должна выглядеть опасной — confirmTone красит подтверждение в тон последствия;
  • фокус ставится на отмену — focusAction не даёт подтвердить опасное действие вслепую по Enter.

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

НужноБерите
Запросить значение, а не согласиеGrPromptDialog
Вызвать подтверждение из кодаGrDialogService
Окно с произвольным содержимымGrDialog
Подтверждение прямо у кнопки, без окнаGrPopover
Сообщить о результате, а не спроситьGrToaster

Подтверждать безобидные действия не нужно: диалог, который всегда отвечают «да», перестаёт читаться и не остановит в тот единственный раз, когда это было важно. Обратимое действие лучше закрывает отмена в тосте.

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

Сцена

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

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

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

  • Отзыв доступа участника

    Ответ назван действием — «Revoke Grace», «Keep access», — потому что «OK» не говорит, на что соглашается, а confirm-tone="danger" красит его в тон последствия. focus-action="cancel" ставит фокус на безопасный ответ: Enter, нажатый сразу после открытия, не должен ничего отзывать.

    Workspace members
    GWGrace WuAccountant
    OHOmar HaddadSales
    INInes NovakOwner
  • Отмена, которую сервер отклоняет

    close-on-confirm="false" держит окно, пока идёт запрос, confirm-loading показывает ожидание в кнопке, а persistent на это время выключает Esc и клик по фону — крестик и «Keep plan» работают. Первая попытка отклонена: error ставит ответ сервера над кнопками, там, где принимается решение.

    Business planRenews on 1 November · €240 a month

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

Props

PropTypeпо умолчаниюОписание
titlestring | undefinedundefinedЗаголовок окна, он же его доступное имя. Не задан — берётся из локали.
size"sm" | "md" | "lg" | "xl" | "full" | undefinedundefinedШирина окна по шкале `GrDialog`: от `sm` до `xl`, `full` — во весь экран. По умолчанию `md`.
descriptionstring | undefinedundefinedТекст вопроса в теле окна. Слот `default` его заменяет.
closeOnBackdropboolean | undefinedtrueЗакрывать по клику на подложку. По умолчанию `true`; при `persistent` на время `confirmLoading` выключается.
closeOnEscboolean | undefinedtrueЗакрывать по Esc. По умолчанию `true`; при `persistent` на время `confirmLoading` выключается.
showHeaderboolean | undefinedtrueПоказывать шапку с заголовком и кнопкой закрытия. По умолчанию `true`.
showCloseButtonboolean | undefinedtrueКнопка закрытия в шапке. По умолчанию `true`; при `persistent` тоже остаётся.
headerConfigGrDialogSectionConfig | undefinedundefinedПоля и линия шапки: `paddingX`, `paddingY`, `bordered`.
footerConfigGrDialogSectionConfig | undefinedundefinedПоля и линия подвала с кнопками.
bodyConfigGrDialogSectionConfig | undefinedundefinedПоля тела окна. `bordered` у тела не применяется.
closeLabelstring | undefinedundefinedA11y-лейбл кнопки закрытия (i18n).
buttonSize"xs" | "sm" | "md" | "lg" | undefinedundefinedРазмер обеих кнопок подвала. Не задан — кнопки берут размер из `GrConfigProvider`.
confirmTextstring | undefinedundefinedПодпись кнопки подтверждения. Не задана — берётся из локали.
cancelTextstring | undefinedundefinedПодпись кнопки отмены. Не задана — берётся из локали.

Слоты

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

События

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

GrConfirmDialogFocusActiontype

Какое действие получает фокус при открытии окна.

type GrConfirmDialogFocusAction = 'confirm' | 'cancel' | 'none';
GrConfirmDialogPropsinterface
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;
}
GrConfirmDialogEmitsinterface
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

grConfirmDialogConfigconst
const grConfirmDialogConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrConfirmDialog">;

Доступность

Клавиши

EscTab / Shift+Tab

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

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

Фокус при открытии

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.

vue
<GrConfirmDialog
  v-model="open"
  confirm-tone="danger"
  :confirm-loading="loading"
  :error="error"
  :close-on-confirm="false"
  persistent
  @confirm="submit"
/>

useDialogService включает persistent всем своим окнам сам — там loading означает, что запрос уже ушёл.