oscript-ui
oscript-ui

Подтверждение

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

Когда использовать#

  • Действие нельзя отменить: удалить проект, отозвать все ключи, передать владение.
  • Ошибка дорогая, и одного щелчка мало: эхо-поле заставляет прочитать, что именно удаляется.

Не спрашивайте подтверждения на обратимом: архивировать, скрыть, переименовать. Лишний вопрос приучает жать «Подтвердить» не читая. Если действие обратимо, сделайте его сразу и дайте тост с отменой. Целый раздел опасных действий в настройках — опасная зона.

Форма спрашивает подтверждение#

Кит.Подтверждение(Заголовок, Последствия) — окно вида «опасное». Форма называет его словом СПодтверждением(Подтверждение): она получает признак 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": по нему сервер
отличает подтверждённую отправку от первой (без скрипта форма с подтверждением приходит
без него — и сервер отвечает страницей с этим окном, напечатанным открытым).

Возвращаемое значение:
 Строка