Подтверждение
Подтверждение — окно перед необратимым действием. Вместо браузера, где только «ОК» и «Отмена», оно называет последствия, просит набрать имя того, что пропадёт, и держит кнопку цвета ошибки недоступной, пока имя не совпало.
Когда использовать#
- Действие нельзя отменить: удалить проект, отозвать все ключи, передать владение.
- Ошибка дорогая, и одного щелчка мало: эхо-поле заставляет прочитать, что именно удаляется.
Не спрашивайте подтверждения на обратимом: архивировать, скрыть, переименовать. Лишний вопрос приучает жать «Подтвердить» не читая. Если действие обратимо, сделайте его сразу и дайте тост с отменой. Целый раздел опасных действий в настройках — опасная зона.
Форма спрашивает подтверждение#
Кит.Подтверждение(Заголовок, Последствия) — окно вида «опасное». Форма называет его словом СПодтверждением(Подтверждение): она получает признак data-confirm, и скрипт кита, поймав её отправку, открывает окно вместо того, чтобы отправить форму. Уходит форма кнопкой окна — та ссылается на неё атрибутом form.
Признак data-confirm обслуживает скрипт кита, и его значение — идентификатор окна подтверждения, а не текст вопроса. Не перехватывайте его своим скриптом: обработчик приложения, который на data-confirm зовёт window.confirm(…), спросит второй раз — родным окном браузера с идентификатором вместо вопроса, — а безголовый браузер такой вопрос отменяет сам, и форма не уходит вовсе. Признак, который не называет окно на странице, кит пропускает: форма уходит как есть. Старому вопросу через window.confirm дайте свой признак с другим именем, а лучше — замените его подтверждением кита.
Удаление проекта — нажмите «Удалить»
Подтверждение = Кит.Подтверждение("Удалить проект «альфа»?",
"Проект, его ключи и история сборок пропадут навсегда. Вернуть их будет нельзя.")
.Эхо("name", "альфа").Кнопка("Удалить проект").Якорь("udalit-alfa");
HTML = Кит.Холст(
Кит.Форма("/проекты/альфа/удалить", Кит.Кнопка("Удалить").Опасная())
.СПодтверждением(Подтверждение),
Подтверждение).Токен("т0кен-сессии").Рендер();Слово Эхо(Имя, Образец) добавляет поле, в которое человек набирает образец. Поле несёт pattern (образец буквально: точка — точка, а не «любой символ») и required, поэтому браузер не отправит форму с несовпавшим образцом даже без скрипта, а лист рисует кнопку недоступной, пока поле не совпало. Скрипт ещё и ставит кнопке disabled. Подпись поля по умолчанию — из словаря интерфейса (confirm.echo), своя — третьим аргументом.
Кнопка подтверждения отправляет поле confirmed=1: по нему сервер отличает подтверждённую отправку от первой. Подпись кнопки — словом Кнопка(Подпись), по умолчанию confirm.ok; отмена — confirm.cancel.
Без скрипта#
Без скрипта форма уходит сразу, без поля confirmed. Сервер на это отвечает перенаправлением на GET того же адреса, и там печатает страницу с этим же окном открытым — Открыто(). Человек набирает образец, жмёт кнопку, форма уходит уже с подтверждением.
Сервер спрашивает подтверждение страницей
<form method="post" action="/проекты/альфа/удалить" id="udalit-alfa-form" data-confirm="udalit-alfa"><input type="hidden" name="_csrf" value="т0кен-сессии"><button type="submit" class="button button--danger"><span>Удалить</span></button></form><dialog class="modal modal--danger modal--confirm" id="udalit-alfa" aria-labelledby="udalit-alfa-title" aria-describedby="udalit-alfa-lead" open><div class="modal__box"><div class="modal__head"><h2 class="modal__title" id="udalit-alfa-title">Удалить проект «альфа»?</h2><button type="button" class="button button--link button--icon" title="Закрыть окно" aria-label="Закрыть окно" command="close" commandfor="udalit-alfa"><svg class="icon" viewBox="0 -960 960 960" aria-hidden="true" focusable="false" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z"></path></svg></button></div><p class="modal__lead" id="udalit-alfa-lead">Вернуть его будет нельзя.</p><div class="modal__body"><div class="modal__echo"><label class="field" for="udalit-alfa-echo"><span class="field__label">Чтобы подтвердить, введите «альфа»</span><input class="field__control" type="text" id="udalit-alfa-echo" name="name" autocomplete="off" required form="udalit-alfa-form" placeholder="альфа" pattern="альфа"></label></div></div><div class="modal__actions"><button type="button" class="button button--ghost" command="close" commandfor="udalit-alfa"><span>Отмена</span></button><button type="submit" class="button button--danger" name="confirmed" value="1" form="udalit-alfa-form"><span>Удалить проект</span></button></div></div></dialog>Подтверждение = Кит.Подтверждение("Удалить проект «альфа»?", "Вернуть его будет нельзя.")
.Эхо("name", "альфа").Кнопка("Удалить проект").Якорь("udalit-alfa")
.Открыто();
HTML = Кит.Холст(
Кит.Форма("/проекты/альфа/удалить", Кит.Кнопка("Удалить").Опасная())
.СПодтверждением(Подтверждение),
Подтверждение).Токен("т0кен-сессии").Рендер();Проверка на клиенте — удобство, гарантия — серверная: сервер сам сверяет эхо-поле с именем и, если не совпало, отвечает 400 той же страницей с окном открытым и отказом под полем — словом Ошибка(Текст).
Эхо-поле закрытого окна печатается заблокированным: оно приписано к форме страницы, и обязательное пустое поле в закрытом окне не дало бы форме уйти вовсе. Открывая окно, скрипт поле открывает, закрывая — блокирует снова.
Своя форма и кнопка#
Подтверждение со своей формой открывает кнопка или пункт меню словом Открывает(Подтверждение), а адрес задаёт слово Форма(Адрес). Подтверждение может лежать и внутри формы — тогда оно отправляет её. Подтверждению без формы нечего отправлять: оно не собирается.
Отзыв ключей — кнопкой
Подтверждение = Кит.Подтверждение("Отозвать все ключи?",
"Сборки и скрипты, которые входят по этим ключам, перестанут работать сразу.")
.Кнопка("Отозвать").Форма("/проекты/альфа/ключи/отозвать").Якорь("otozvat");
HTML = Кит.Холст(Кит.Кнопка("Отозвать все ключи").Опасная().Открывает(Подтверждение),
Подтверждение).Токен("т0кен-сессии").Рендер();Подтверждение, открытое по адресу, закрывает без скрипта слово АдресЗакрытия(Адрес) (дев-сборка 8): «Отмена» и крестик печатаются ссылками на страницу без окна, а со скриптом закрывают окно на месте — см. окно.
Длинное слово без пробелов — адрес, ключ, хеш — переносится в тексте окна по знакам (дев-сборка 8): окно не бывает шире экрана, а адрес не обрезается его краем.
Длинный адрес в последствиях — окно не шире экрана
Адрес = "https://mirror.example.org/api/v1/pools/public/download/oscript/";
Подтверждение = Кит.Подтверждение("Удалить зеркало?",
"Зеркало " + Адрес + " перестанет получать пакеты, его журнал пропадёт.")
.Эхо("mirror", Адрес).Кнопка("Удалить зеркало").Якорь("udalit-zerkalo");
HTML = Кит.Холст(
Кит.Форма("/зеркала/1/удалить", Кит.Кнопка("Удалить зеркало").Опасная())
.СПодтверждением(Подтверждение),
Подтверждение).Токен("т0кен-сессии").Рендер();Тот же поток с настоящим сервером — в образце со списком проектов: удаление спрашивает имя проекта, отказ сервера остаётся в окне, удача закрывает окно и убирает строку.
Гайдлайны#
Код
Подтверждение = Кит.Подтверждение("Удалить проект «альфа»?",
"Проект, его ключи и история сборок пропадут навсегда.")
.Кнопка("Удалить проект").Форма("/проекты/альфа/удалить").Якорь("delat-posledstviya");
HTML = Кит.Холст(Подтверждение).Токен("т0кен-сессии").Рендер();Код
Подтверждение = Кит.Подтверждение("Вы уверены?", "Продолжить?")
.Форма("/проекты/альфа/удалить").Якорь("ne-delat-uvereny");
HTML = Кит.Холст(Подтверждение).Токен("т0кен-сессии").Рендер();Код
Подтверждение = Кит.Подтверждение("Отозвать ключ «ноутбук»?", "Вход по этому ключу перестанет работать.")
.Кнопка("Отозвать ключ").Форма("/ключи/ноутбук/отозвать").Якорь("delat-knopka");
HTML = Кит.Холст(Подтверждение).Токен("т0кен-сессии").Рендер();Код
Подтверждение = Кит.Подтверждение("Перенести проект в архив?", "Проект можно вернуть из архива.")
.Форма("/проекты/альфа/архив").Якорь("ne-delat-arhiv");
HTML = Кит.Холст(Подтверждение).Токен("т0кен-сессии").Рендер();Доступность#
- Окно названо вопросом-заголовком, последствия — его описание (
aria-describedby): скринридер читает их сразу за заголовком. - Заголовок и кнопка — цветом ошибки, но смысл несут слова, а не цвет: «Удалить проект», а не красный «ОК».
- Эхо-поле подписано, что набрать; отказ сервера — под полем с
role="alert", поле помеченоaria-invalid. - Отмена — первой и вторичной: Esc и «Отмена» закрывают окно, ничего не отправив, и фокус возвращается туда, откуда открыли.
Модель#
Кит.Подтверждение()src/Классы/холст/Кит.os
Подтверждение необратимого действия — окно вида «опасное» вместо window.confirm: заголовок, последствия (описание окна для скринридера), дети узла под ними, необязательное эхо-поле и кнопка цвета ошибки рядом с отменой. Отправляет свою форму (слово Форма), форму, которая назвала его словом СПодтверждением(узел), или форму вокруг. Слова: Эхо(Имя, Образец, Метка, Подсказка), Кнопка(Подпись), Форма(Адрес), Открыто, Якорь, БезПерезагрузки, Токен. Параметры: Заголовок - Строка - вопрос окна («Удалить проект?»). Последствия - Строка - что случится и что нельзя будет вернуть. Р1…Р7 - ЭлементЭкрана, Строка - (необязательные) дополнение под последствиями. Возвращаемое значение: ЭлементЭкрана
ЭлементЭкрана.Эхо()src/Классы/холст/ЭлементЭкрана.os
Эхо-поле подтверждения: человек набирает образец, и только тогда кнопка подтверждения становится доступной. Поле несёт pattern и required — без скрипта браузер не отправит форму с несовпавшим образцом; сервер сверяет значение поля сам. Параметры: Имя_ - Строка - имя поля (его значение сверяет сервер). Образец - Строка - что человек обязан набрать. Метка_ - Строка - (необязательный) подпись поля; по умолчанию из словаря «confirm.echo». Подсказка_ - Строка - (необязательный) подсказка под полем. Возвращаемое значение: ЭлементЭкрана - этот же элемент.
ЭлементЭкрана.СПодтверждением()src/Классы/холст/ЭлементЭкрана.os
Форма спрашивает подтверждение перед отправкой: признак data-confirm, скрипт перехватывает
отправку и открывает окно, а кнопка окна отправляет эту форму (атрибут form). Без скрипта
форма уходит как есть — без поля подтверждения (Подтверждение.ИмяПоляПодтверждения), и
сервер отвечает страницей с этим же окном, напечатанным открытым. Признак обслуживает
скрипт кита, значение — идентификатор окна: свой window.confirm на data-confirm приложение
не вешает, иначе человека спросят дважды, второй раз — идентификатором вместо вопроса.
Параметры:
Окно_ - ЭлементЭкрана, Строка - узел Кит.Подтверждение (или Кит.Окно) либо его
идентификатор (якорь).
Возвращаемое значение:
ЭлементЭкрана - эта же форма.Подтверждение.Отрисовать()src/Классы/холст/Подтверждение.os
Печатает окно подтверждения. Последствия — описание окна (aria-describedby): скринридер
читает их сразу за заголовком. Кнопка подтверждения отправляет форму с полем
ИмяПоляПодтверждения() = "1", чтобы сервер отличил подтверждённую отправку от первой.
Параметры:
Модель - Структура - поля рамки окна (см. Окно.Отрисовать) и Последствия (что
случится, необратимо), Дополнение (ДОВЕРЕННЫЙ HTML под последствиями), Эхо
(Структура: Имя, Образец, Метка, Подсказка; Неопределено — эхо-поля нет),
ОшибкаЭха (отказ под эхо-полем),
Кнопка (подпись кнопки подтверждения; пусто — из словаря «confirm.ok»),
ФормаИд (идентификатор формы вне окна, которую отправляет кнопка; пусто —
своя форма окна либо форма вокруг).
Возвращаемое значение:
Строка - HTML <dialog>.Подтверждение.ИмяПоляПодтверждения()src/Классы/холст/Подтверждение.os
Имя поля, которое кнопка подтверждения отправляет со значением "1": по нему сервер отличает подтверждённую отправку от первой (без скрипта форма с подтверждением приходит без него — и сервер отвечает страницей с этим окном, напечатанным открытым). Возвращаемое значение: Строка