oscript-ui
oscript-ui

Дом настроек

Единый каркас всех экранов настроек: объект, дерево разделов, секции и панель сохранения. Дом решает, что показать, каркас — как.

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

  • У объекта — проекта, команды, учётной записи — есть настройки, и их больше одной формы.
  • Настройки делятся на разделы: общее, доступ, ключи, история.
  • Каждая группа полей сохраняется отдельно и проверяется отдельно.

Для одной короткой формы дом не нужен: хватит полей и кнопки в карточке.

Устройство#

Дом — узел Кит.ДомНастроек(Заголовок, Адрес, Токен, секции…). Слева — разделы дома деревом, справа — секции текущего раздела. Разделы объявляет слово Раздел(Подпись, Ключ, Иконка): адрес раздела — адрес дома, косая черта и ключ; пустой ключ — адрес самого дома, ключ с ведущей косой — адрес как есть. Текущий раздел помечает слово Текущий().

Секции — дети дома, узлы Кит.Секция(Заголовок, тело…). Секция-форма печатается своей формой со своей панелью сохранения: секция — единица переноса, у неё своя проверка и свой POST.

Разделы, секции и шторка узкого экрана

Настройкиальфа

Что видит участник проекта

Общее

Имя проекта

Видно всем участникам

Дом = Кит.ДомНастроек("Настройки", "/настройки", "т0кен-сессии", Кит.Секция("Имя проекта", Кит.Поле("имя", "Имя").Значение("альфа")) .Лид("Видно всем участникам").Форма()) .Ключ("project").Объект("альфа").Пояснение("Что видит участник проекта"); Дом.Раздел("Общее", "", "шестерёнка").Текущий(); Дом.Раздел("Доступ", "доступ", "замок"); Дом.Раздел("Ключи", "ключи", "ключ"); Дом.Раздел("История", "история", "история"); HTML = Дом.Рендер();

Рубрики и опасный раздел#

Длинный дом читается группами. Слово дома Рубрика(Подпись) ставит подпись над следующими разделами: все Раздел(…) после неё, до следующей рубрики, встают под неё. Рубрика("") заканчивает группу. Разделы не переставляются — порядок остаётся тот, в котором их объявил дом.

Раздел необратимого — удаление, передача — помечает слово раздела Опасный(). Он печатается цветом ошибки, значок тоже, и всегда стоит последним и без рубрики, где бы его ни объявили: необратимое не должно стоять между обычными разделами. Слово Счётчик(Число, Подпись) ставит число у пункта раздела в едином меню — например, сколько заявок ждёт ответа.

Три рубрики и опасный раздел

Настройки проектаальфа

Участники

Участники

Пригласите коллег по почте.

Дом = Кит.ДомНастроек("Настройки проекта", "/проекты/альфа/настройки", "т0кен-сессии", Кит.Секция("Участники", Кит.Примечание("Пригласите коллег по почте."))) .Ключ("project").Объект("альфа"); Дом.Раздел("Общее", "", "шестерёнка"); Дом.Рубрика("Доступ"); Дом.Раздел("Участники", "участники", "аккаунт").Текущий().Счётчик(2, "2 заявки"); Дом.Раздел("Ключи", "ключи", "ключ"); Дом.Рубрика("Оповещения"); Дом.Раздел("Почта", "почта", "конверт"); Дом.Рубрика("Журнал"); Дом.Раздел("История", "история", "история"); Дом.Раздел("Удаление", "удаление", "корзина").Опасный(); HTML = Дом.Рендер();

На телефоне#

Меню на телефоне одно — бургер единого меню. Разделы дома встают в его шторку первой группой, сразу под учёткой: заголовок группы — имя дома, внутри те же рубрики и пункты, что в колонке, текущий раздел залит, опасный — цветом ошибки. Остальные группы шторки — дороги приложения, дела, тема и выход — идут ниже и не двигаются от страницы к странице. Колонка разделов на странице прячется: второго меню рядом с бургером нет.

Чтобы было видно, где человек стоит, под заголовком дома печатается имя и значок текущего раздела — строкой settings__current, а не ссылкой. На широком экране эта строка скрыта: там колонка разделов с подсвеченным пунктом, а в выпадающем меню у кнопки-учётки группы разделов нет. Всё это делает лист стилей, скрипта здесь нет.

Если единого меню у приложения нет, на телефоне колонка разделов встаёт над секциями и читается пальцем, а строка с именем раздела не нужна и скрыта.

Каркас страницы берёт разделы для меню у самого дома, когда содержимое страницы — дом: передавать их вторым аргументом незачем. Группу он называет заголовком дома.

Дом = Кит.ДомНастроек("Настройки проекта", "/проекты/альфа/настройки", Токен, Секции…);
Дом.Раздел("Общее", "").Текущий();
Ответ.ТелоТекст = Каркас.Страница("Настройки проекта", Дом, Контекст);

Имя текущего раздела под заголовком — на телефоне с единым меню

Настройки проектаальфа

Ключи

Ключи

Ключей пока нет.

Дом = Кит.ДомНастроек("Настройки проекта", "/проекты/альфа/настройки", "т0кен-сессии", Кит.Секция("Ключи", Кит.Примечание("Ключей пока нет.").Вид("пусто"))) .Ключ("project").Объект("альфа"); Дом.Раздел("Общее", "", "шестерёнка"); Дом.Раздел("Ключи", "ключи", "ключ").Текущий(); HTML = Дом.Рендер();

Сузьте окно этого сайта до телефона: у образца выше колонка разделов спрячется, а под заголовком встанет «Ключи» со значком — у сайта есть единое меню.

Дом без разделов — ни колонки, ни шторки

Настройки

Единственная секция

прятать нечего: разделов у дома нет

HTML = Кит.ДомНастроек("Настройки", "/настройки", "", Кит.Секция("Единственная секция", Кит.Примечание("прятать нечего: разделов у дома нет"))) .Ключ("project").Рендер();

Над шапкой дома — крошки, над секциями — уведомления: слова Крошка(Подпись, Адрес) и Уведомление(узел или текст). Крошки печатает компонент «Крошки»: последняя — текущая страница, текстом и без ссылки. Слово Пояснение необязательно: у короткого дома его нет, и пустого абзаца под заголовком тоже нет.

Крошки и уведомление над секциями

Ключи доступа

Ключ отозван

Ключи

Ключей пока нет.

HTML = Кит.ДомНастроек("Ключи доступа", "/проекты/альфа/ключи", "т0кен-сессии", Кит.Секция("Ключи", Кит.Примечание("Ключей пока нет.").Вид("пусто"))) .Ключ("project") .Крошка("Проекты", "/проекты").Крошка("альфа", "/проекты/альфа").Крошка("Ключи") .Уведомление(Кит.Уведомление("Ключ отозван").Вид("warn")) .Рендер();

Адрес и токен — у дома#

Адрес раздела и CSRF-токен задаются один раз — у дома. Секция-форма и любая форма внутри берут их у ближайшего предка:

  • Секция.Форма() — форма на адрес текущего раздела, Секция.Форма("описание") — на раздел/описание, Секция.Форма("/x") — на свой адрес;
  • Кит.Форма("", …), Кит.Форма("revoke", …), Кит.Форма("/x", …) внутри секции — по тем же правилам.

Форма без токена ни у себя, ни у предков не печатается: она выглядела бы рабочей, а получала 403.

Секция сохраняется своей формой

Настройки

Описание

Короткий текст под именем проекта

Изменения увидят все участники
HTML = Кит.ДомНастроек("Настройки", "/настройки", "т0кен-сессии", Кит.Секция("Описание", Кит.Поле("описание", "Описание").Многострочное().Значение("Отчёты команды аналитики")) .Лид("Короткий текст под именем проекта") .Форма("описание").ПояснениеПанели("Изменения увидят все участники")) .Ключ("project").Рендер();

Секция без формы — витрина или список, где у каждой строки своя маленькая форма. Строки таблицы из данных находят адрес и токен дома так же, как дети секции.

Секция-список: у каждой строки своя форма

Настройки

Ключи

Мои ключи

ИмяДействие
ноутбук
сборка
Ключи = Новый ТаблицаЗначений; Ключи.Колонки.Добавить("Имя"); Ключи.Колонки.Добавить("Действие"); Для Каждого Имя Из СтрРазделить("ноутбук,сборка", ",") Цикл Ключ = Ключи.Добавить(); Ключ.Имя = Имя; Ключ.Действие = Кит.Форма(Имя + "/отзыв", Кит.Кнопка("Отозвать").Опасная().Малая()); КонецЦикла; Дом = Кит.ДомНастроек("Настройки", "/проекты/альфа/настройки", "т0кен-сессии", Кит.Секция("Мои ключи", Кит.Таблица(Ключи).Пусто("Ключей пока нет.")).Иконка("ключ")) .Ключ("project"); Дом.Раздел("Ключи", "ключи").Текущий(); HTML = Дом.Рендер();

Действия секции#

Кнопка, которая добавляет запись в список секции, стоит в одном месте на всех экранах — справа от заголовка и лида секции, по верху. Её кладут во второй холст секции Действия(д…): кнопки, ссылки-кнопки или маленькую форму с одной кнопкой. Форма в слоте берёт адрес и токен у дома, как и форма в теле секции. На узком экране (до 640 px) слот встаёт под лидом во всю ширину, и кнопка растягивается.

Кнопка «Добавить» справа от заголовка

Настройки

Ключи

Мои ключи

Ключом участник входит без пароля

ИмяСоздан
ноутбук12 марта
Ключи = Новый ТаблицаЗначений; Ключи.Колонки.Добавить("Имя"); Ключи.Колонки.Добавить("Создан"); Ключ = Ключи.Добавить(); Ключ.Имя = "ноутбук"; Ключ.Создан = "12 марта"; Дом = Кит.ДомНастроек("Настройки", "/проекты/альфа/настройки", "т0кен-сессии", Кит.Секция("Мои ключи", Кит.Таблица(Ключи).Пусто("Ключей пока нет.")) .Лид("Ключом участник входит без пароля") .Действия(Кит.Форма("новый", Кит.Кнопка("Добавить ключ").Основная().Иконка("ключ")))) .Ключ("project"); Дом.Раздел("Ключи", "ключи").Текущий(); HTML = Дом.Рендер();

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

Действия структурой — прежний вид модели

Участники

Кто работает над проектом

Пока в проекте только вы.

Действия = Новый Массив(); Действия.Добавить(Новый Структура("Подпись, Адрес, Вариант", "Пригласить", "/участники/новый", "вторичная")); Секция = Настройки.Секция("Участники", "Кто работает над проектом", Кит.Примечание("Пока в проекте только вы.").Рендер()); Секция.Действия = Действия; HTML = Настройки.ОтрисоватьСекцию(Секция);

Опасная секция

Настройки

Удаление

Проект и все его документы будут удалены

Удалить проект

Отменить это нельзя.

HTML = Кит.ДомНастроек("Настройки", "/настройки", "", Кит.Секция("Удаление", Кит.ОпаснаяЗона("Удалить проект").Пояснение("Отменить это нельзя.") .Подтверждение("подтверждение", "альфа")) .Лид("Проект и все его документы будут удалены").Опасная().БезПанели()) .Ключ("project").Рендер();

Секция, которую открывают редко, уходит под кат целиком словом Свёрнута(ПодписьРаскрытия); Раскрыта() печатает кат открытым — так отбитая форма возвращается человеку.

Свёрнутая секция-форма

Настройки

Передача проекта

Передать проект другому участнику
HTML = Кит.ДомНастроек("Настройки", "/настройки", "т0кен-сессии", Кит.Секция("Передача проекта", Кит.Поле("кому", "Новый владелец")) .Форма("передача").Свёрнута("Передать проект другому участнику").Кнопка("Передать")) .Ключ("project").Рендер();

Секция вне дома#

Секция печатается и сама — тем же каркасом, что внутри дома. Секции-форме вне дома нужен свой адрес от корня, токен она берёт у предков.

Секция вне дома

Почта для писем

HTML = Кит.Холст( Кит.Секция("Почта для писем", Кит.Поле("почта", "Почта").ТипЗначения("email")) .Форма("/учётка/почта")).Токен("т0кен-сессии").Рендер();

Прежний вид модели#

Настройки.Отрисовать(Модель) со структурой дома, Настройки.Раздел и Настройки.Секция работают как раньше и печатают ту же разметку, что дом из узлов.

Дом структурой — прежний вид модели

Настройки

Описание

Короткий текст под именем проекта

Изменения увидят все участники
Секция = Настройки.Секция("Описание", "Короткий текст под именем проекта", Набор.Поле().Отрисовать(Новый Структура("Имя, Метка, Тип, Значение", "описание", "Описание", "многострочное", "Отчёты команды аналитики"))); Секция.Действие = "/настройки/описание"; Секция.Токен = "т0кен-сессии"; Секция.ПояснениеПанели = "Изменения увидят все участники"; Секции = Новый Массив(); Секции.Добавить(Секция); HTML = Настройки.Отрисовать(Новый Структура("Ключ, Заголовок, Секции", "project", "Настройки", Секции));

Гайдлайны#

Настройки

Имя проекта

Ключ доступа

ДелайтеОдна секция — одна тема и своя кнопка сохранения
Код
HTML = Кит.ДомНастроек("Настройки", "/настройки", "т",
	Кит.Секция("Имя проекта", Кит.Поле("имя", "Имя").Значение("альфа")).Форма("имя"),
	Кит.Секция("Ключ доступа", Кит.Поле("ключ", "Ключ доступа")).Форма("ключ"))
	.Ключ("project").Рендер();

Настройки

Всё сразу

Не делайтеВсе поля дома одной формой: ошибка в одном поле откатывает остальные
Код
HTML = Кит.ДомНастроек("Настройки", "/настройки", "т",
	Кит.Секция("Всё сразу",
		Кит.Поле("имя", "Имя"),
		Кит.Поле("ключ", "Ключ доступа"),
		Кит.Поле("почта", "Почта для писем")).Форма())
	.Ключ("project").Рендер();

Доступность#

  • Колонка разделов — навигация с подписью из словаря инсталляции (settings.frame.nav.title); текущий раздел помечен aria-current. Рубрика — строка списка с текстом, а не ссылка: скринридер читает её на своём месте, перед разделами группы.
  • Опасный раздел отличается не только цветом: он стоит последним, а его подпись говорит, что он делает («Удаление»).
  • Строка с именем текущего раздела — обычный абзац: она отвечает на «где я» и в порядок обхода клавиатурой не встаёт.
  • Секция — <section> с заголовком: скринридер ходит по секциям, как по главам.
  • Ключ дома пишется латиницей: он уходит в атрибут data-house, а имена атрибутов кит пропускает только из [a-z0-9-].

Справка#

Кит.ДомНастроек()src/Классы/холст/Кит.os

Дом настроек — объект, разделы колонкой слева и секции справа; печатается каркасом
настроек (РендерерНастроек) — разметка та же, что у модели-структуры. Адрес и токен
задаются один раз здесь: секции-формы и формы внутри берут их у дома. Дети — секции
(Кит.Секция); разделы — словом Раздел(Подпись, Ключ, Иконка), рубрики над ними — словом
Рубрика(Подпись). Слова: Ключ (латиницей, data-house), Объект, Пояснение,
Крошка(Подпись, Адрес), Уведомление(узел | текст), Рубрика, Раздел.

Параметры:
 Заголовок - Строка - заголовок дома.
 Адрес     - Строка - адрес дома; адрес раздела — Адрес/Ключ.
 Токен     - Строка, ЭлементЭкрана - (необязательный) CSRF-токен сессии; узел на этом
             месте — первая секция.
 Р1…Р7     - ЭлементЭкрана - (необязательные) секции.

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

Кит.Секция()src/Классы/холст/Кит.os

Секция настроек — карточка с заголовком; её тело — дети узла, кнопки справа от
заголовка — второй холст Действия(д…) (на узком экране он встаёт под лидом). Внутри
дома и вне его печатается тем же каркасом. Словом Форма(Хвост) становится формой со
своей панелью сохранения: адрес — у контекста, токен — у предков. Слова: Лид, Иконка,
Форма, Кнопка, ИконкаКнопки, ИмяКнопки(Имя, Значение), ПояснениеПанели, АдресОтмены,
Опасная, Заблокирована, БезПанели, СФайлами, Свёрнута(ПодписьРаскрытия), Раскрыта, Якорь.

Параметры:
 Заголовок - Строка - заголовок секции.
 Р1…Р8     - ЭлементЭкрана, Строка - (необязательные) тело секции; строка — текст.

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

ЭлементЭкрана.Раздел()src/Классы/холст/ЭлементЭкрана.os

Раздел дома настроек: строка в колонке разделов. Адрес раздела — адрес дома, косая черта
и ключ; ключ с ведущей косой берётся адресом как есть, пустой — адрес самого дома.
Раздел попадает под рубрику, объявленную словом Рубрика дома до него.

Параметры:
 Подпись - Строка - название раздела.
 Ключ    - Строка - хвост адреса раздела.
 Иконка  - Строка - (необязательный) имя иконки.

Возвращаемое значение:
 ЭлементЭкрана - узел раздела: слова Текущий(), Опасный(), Счётчик(Число, Подпись).

ЭлементЭкрана.Текущий()src/Классы/холст/ЭлементЭкрана.os

Раздел дома — тот, что открыт: он помечен в колонке разделов, и его адрес — адрес
контекста для форм дома. Шаг формы (Кит.Шаг) — тот, с которого шаги начинаются: открыт
без скрипта и текущий со скриптом, если ни у одного поля нет ошибки.

Возвращаемое значение:
 ЭлементЭкрана - этот же элемент.

ЭлементЭкрана.Рубрика()src/Классы/холст/ЭлементЭкрана.os

Рубрика дома: все разделы, объявленные словом Раздел после неё, — до следующей
рубрики — встают под эту подпись в колонке разделов и в группе разделов единого меню.
Пустая подпись заканчивает рубрику: следующие разделы идут без неё.

Параметры:
 Подпись_ - Строка - подпись рубрики.

Возвращаемое значение:
 ЭлементЭкрана - этот же дом.

ЭлементЭкрана.Опасный()src/Классы/холст/ЭлементЭкрана.os

Раздел необратимого (удаление, передача): цветом ошибки, значок тоже, всегда последним
и без рубрики — где бы его ни объявили.

Параметры:
 Флаг - Булево - (необязательный, по умолчанию Истина)

Возвращаемое значение:
 ЭлементЭкрана - этот же раздел.

Кит.ЗаголовокДома()src/Классы/холст/Кит.os

Заголовок дома — им каркас страницы подписывает группу разделов дома в едином меню.

Параметры:
 Дом - ЭлементЭкрана - узел Кит.ДомНастроек.

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

ЭлементЭкрана.Форма()src/Классы/холст/ЭлементЭкрана.os

Секция становится формой со своей панелью сохранения; окно — формой целиком (кнопки
действий отправляют её сами). Адрес — адрес контекста (раздел дома) с хвостом: пустой
хвост — сам адрес, «x» — адрес/x, «/x» — абсолютный адрес. Токен берётся у предков.

Параметры:
 Хвост - Строка - (необязательный) хвост адреса.

Возвращаемое значение:
 ЭлементЭкрана - этот же элемент.

ЭлементЭкрана.Действия()src/Классы/холст/ЭлементЭкрана.os

Действия — второй холст: кнопки и формы. У карточки они печатаются под её телом,
у секции настроек — справа от заголовка и лида (на узком экране — под лидом).

Параметры:
 Р1 - ЭлементЭкрана, Строка, Неопределено - (необязательный) действия, до Р8.
 Р2 - ЭлементЭкрана, Строка, Неопределено - (необязательный)
 Р3 - ЭлементЭкрана, Строка, Неопределено - (необязательный)
 Р4 - ЭлементЭкрана, Строка, Неопределено - (необязательный)
 Р5 - ЭлементЭкрана, Строка, Неопределено - (необязательный)
 Р6 - ЭлементЭкрана, Строка, Неопределено - (необязательный)
 Р7 - ЭлементЭкрана, Строка, Неопределено - (необязательный)
 Р8 - ЭлементЭкрана, Строка, Неопределено - (необязательный)

Возвращаемое значение:
 ЭлементЭкрана - без аргументов холст действий; с аргументами — эта карточка или секция.

Кит.РазделыДома()src/Классы/холст/Кит.os

Разделы дома для единого меню — та же форма, что у РендерерНастроек.Раздел: каркас
страницы берёт их отсюда, когда содержимое страницы — дом.

Параметры:
 Дом - ЭлементЭкрана - узел Кит.ДомНастроек.

Возвращаемое значение:
 Массив из Структура - Подпись, Адрес, Иконка, Активен, Рубрика, Опасный, Счётчик,
                       ПодписьСчётчика.

РендерерНастроек.Отрисовать()src/Классы/каркас/РендерерНастроек.os

Отрисовывает дом настроек.

Параметры:
 Модель - Структура - Ключ дома ЛАТИНИЦЕЙ ("account", "project", "team"):
          он идёт в data-house, а ЭкранированиеHTML отбрасывает имена атрибутов вне
          [a-z0-9-]; Заголовок, Объект (имя настраиваемого объекта), Пояснение,
          Разделы (модель ДеревоРазделов, см. Раздел), Уведомления (структуры Текст,
          Вид для компонента «Тост»), Секции (см. Секция), Хлебные (структуры
          Подпись, Адрес — печатаются компонентом «Крошки»: последняя крошка — текущая
          страница, без ссылки). Пояснение необязательно: без него абзаца под
          заголовком нет вовсе.

Возвращаемое значение:
 Строка - HTML-фрагмент для <main>. Под заголовком дома, у которого есть текущий
          раздел, стоит его имя и значок (settings__current): на телефоне колонку
          разделов заменяет единое меню, и страница сама говорит, где стоит человек.

РендерерНастроек.Раздел()src/Классы/каркас/РендерерНастроек.os

Заготовка раздела дерева для модели дома. Поля Рубрика и Опасный стоят в структуре
всегда, поэтому их можно дописать и у готового результата.

Параметры:
 Подпись - Строка - название раздела.
 Адрес   - Строка - URL раздела.
 Иконка  - Строка - (необязательный, по умолчанию "") имя иконки.
 Активен - Булево - (необязательный, по умолчанию Ложь) текущий раздел.
 Рубрика - Строка - (необязательный, по умолчанию "") подпись над первым разделом
           с этим именем рубрики.
 Опасный - Булево - (необязательный, по умолчанию Ложь) раздел необратимого: цветом
           ошибки, последним и без рубрики.

Возвращаемое значение:
 Структура - раздел для ДеревоРазделов: Подпись, Адрес, Иконка, Активен, Рубрика, Опасный.

РендерерНастроек.Секция()src/Классы/каркас/РендерерНастроек.os

Заготовка секции. Поля дозаполняются у результата — структура открытая.

Параметры:
 Заголовок - Строка - заголовок секции.
 Пояснение - Строка - (необязательный, по умолчанию "") что настраивает секция.
 Тело      - Строка - (необязательный, по умолчанию "") ДОВЕРЕННЫЙ HTML содержимого.

Возвращаемое значение:
 Структура - секция со всеми известными каркасу полями: Действие (URL POST; задан —
             секция становится <form>), Токен (CSRF-токен, обязателен при Действии),
             Кнопка, ИконкаКнопки (пусто — «сохранить»), ИмяКнопки и ЗначениеКнопки
             (name/value кнопки панели: форма с двумя кнопками отличает нажатую),
             АдресОтмены, ПояснениеПанели, Опасная, Заблокирована, БезПанели
             (кнопки внутри тела), Иконка, Класс, Якорь, СФайлами (форма секции
             получает enctype составной формы — без него браузер шлёт одни имена
             файлов вместо содержимого), Свёрнута (секция уходит под кат целиком),
             ПодписьРаскрытия (обязательна у свёрнутой), Раскрыта (кат печатается
             открытым — так возвращается отбитая форма) и Действия (кнопки справа от
             заголовка и лида: массив моделей компонента «Кнопка» либо ДОВЕРЕННЫЙ HTML
             строкой; пусто — слота нет вовсе).

РендерерНастроек.ОтрисоватьСекцию()src/Классы/каркас/РендерерНастроек.os

Отрисовывает одну секцию — та же разметка, что секция внутри дома: карточка-<section>,
своя форма с CSRF-полем и панелью сохранения, когда у секции есть Действие. Так секция
печатается и вне дома настроек.

Параметры:
 ОписаниеСекции - Структура - секция (см. Секция).

Возвращаемое значение:
 Строка - HTML-фрагмент <section>.