Сегменты
Сегменты — выбор одного из немногих вариантов рядом кнопок в одной пилюле: тема, режим, пресет. Все варианты видны сразу, выбранный залит цветом и отмечен галкой. Под капотом — радиокнопки одного имени: выбор уходит с формой без скрипта, стрелки меняют его, а наборы полей под сегментами переключает лист.
Когда использовать#
- Вариантов два–четыре, подписи короткие, и человеку полезно видеть их все сразу: «Светлая / Тёмная / Авто».
- От выбора зависит, какие поля заполнять дальше: «Ключ / Пароль» — у каждого способа свои поля.
- Выбор в строке списка, который сохраняется сам: роль участника, режим уведомлений.
Вариантов больше пяти или подписи длинные — это поле выбора (Кит.Поле(…).Вариант(…)). Два состояния «включено / выключено» — тумблер. Соседние виды одного объекта со своими адресами — вкладки: сегменты ничего не открывают, они выбирают значение.
Выбор#
Сегменты — Кит.Сегменты(Имя, …) из Кит.Сегмент(Значение, Подпись). Имя — имя поля формы: под ним уходит значение выбранного сегмента. Отмечен ровно один: сегмент со словом Выбран(), иначе тот, чьё значение назвало слово Выбрано(Значение), иначе первый. Подпись — имя группы для скринридера.
Тема интерфейса
HTML = Кит.Форма("/настройки/тема",
Кит.Сегменты("theme",
Кит.Сегмент("light", "Светлая"),
Кит.Сегмент("dark", "Тёмная"),
Кит.Сегмент("auto", "Авто")).Подпись("Тема").Выбрано("auto"),
Кит.Кнопка("Сохранить").Основная()).Токен("т0кен-сессии").Рендер();Значение, которого нет среди сегментов, отмечает первый — так же браузер показывает выпадающий список, чьё значение не нашлось. Два сегмента с одним значением сборка не пускает: сервер получил бы одно и то же, какой ни выбери.
Значок у сегмента#
Слово сегмента Иконка(Имя) ставит значок перед подписью — глиф набора или логотип бренда (бренд:telegram): канал уведомлений, способ входа (дев-сборка 10). Значок декоративен — что выбрано, говорит подпись; галка выбранного встаёт перед ним. На телефоне подпись переносится, а значок остаётся.
Канал подписки — сегменты со значками
HTML = Кит.Сегменты("канал",
Кит.Сегмент("webhook", "Вебхук").Иконка("вебхук"),
Кит.Сегмент("telegram", "Telegram").Иконка("бренд:telegram"),
Кит.Сегмент("email", "Почта").Иконка("конверт")).Подпись("Канал").Выбрано("telegram").Рендер();Наборы полей#
Дети сегмента — его набор полей: под группой виден набор отмеченного сегмента, остальные спрятаны. Переключает их лист кита (:has(:checked)), без скрипта и без запроса на сервер. Сегмент без детей — без набора: под ним пусто.
Способ входа — у каждого свои поля
HTML = Кит.Форма("/подключения",
Кит.Сегменты("auth",
Кит.Сегмент("key", "Ключ",
Кит.Поле("token", "Ключ доступа").Подсказка("выдаёт владелец проекта")),
Кит.Сегмент("password", "Пароль",
Кит.Поле("login", "Логин"),
Кит.Поле("secret", "Пароль").Секрет()),
Кит.Сегмент("none", "Без входа")).Подпись("Способ входа"),
Кит.Кнопка("Подключить").Основная()).Токен("т0кен-сессии").Рендер();Как это связано. Сегменты печатают под группой по панели на каждый сегмент, в том же порядке, — <div class="segments__panel" data-segment="значение"> с его набором внутри. Лист показывает панель номер N, пока отмечен сегмент номер N; правила перечислены до восьми сегментов, и девятый набор сборка не пускает. Признак data-segment несёт значение сегмента: по нему свой CSS или скрипт страницы находит набор, не считая номеров.
Спрятанный набор не отключён: его поля уходят с формой вместе с выбранным, и сервер берёт поля того способа, который пришёл в имени сегментов. Движок без :has() показывает все наборы сразу — поля не теряются.
Панель вне набора#
Набор полей стоит прямо под группой. Когда область должна появиться в другом месте той же формы — в следующем шаге мастера, в другой группе, — её печатают отдельно: Кит.ПанельСегмента(ИмяСегментов, Номер, д…) или короче словом самих сегментов Сегменты.Панель(Номер, д…). Панель видна, пока в той же форме отмечен сегмент номер Номер (с 1) этих сегментов; вместо номера можно дать значение сегмента строкой. Переключает её лист, без скрипта и без запроса, тем же приёмом, что и наборы.
Канал выбирают в первом шаге, а текст сообщения — во втором, только у канала с текстом
Канал = Кит.Сегменты("channel",
Кит.Сегмент("chat", "Чат"),
Кит.Сегмент("mail", "Почта"),
Кит.Сегмент("hook", "Вебхук")).Подпись("Канал").Выбрано("chat");
HTML = Кит.Форма("/подписки",
Кит.Шаги(
Кит.Шаг("Куда", Канал, Кит.Поле("target", "Адрес").Обязательное()),
Кит.Шаг("Текст",
Канал.Панель(1, Кит.Поле("chat_text", "Текст в чат").Многострочное()
.Значение("Новая версия {проект} {версия}")),
Кит.ПанельСегмента("channel", "mail",
Кит.Поле("mail_subject", "Тема письма").Значение("Вышла {версия}"),
Кит.Поле("mail_text", "Текст письма").Многострочное()),
Кит.ПанельСегмента("channel", 3,
Кит.Примечание("Вебхук получает событие целиком, текст ему не нужен."))))
.Кнопка("Создать подписку")).Токен("т0кен-сессии").Рендер();Как это связано. Панель печатается <div class="segments-panel" data-segments-slot="1" data-segments-number="N" data-segment="значение">, а группа сегментов, на которую ссылаются панели, получает тот же data-segments-slot. Это место группы среди сегментов формы, у которых есть панели: лист не сравнит имя поля панели с именем радиокнопок, поэтому сервер нумерует такие группы, а правила перечислены по месту — до четырёх групп с панелями в форме — и по номеру сегмента, до восьми. Без панелей разметка сегментов прежняя.
Панель и её сегменты лежат в одной форме — Кит.Форма, окно или секция со словом Форма: панель вне формы, панель без своих сегментов в форме и номер больше числа сегментов останавливают сборку. Как и у наборов, спрятанная панель не отключена: её поля уходят с формой, лишние отбрасывает сервер, и обязательное поле в панели, которая может быть спрятана, браузер не пропустит — делайте такие поля обязательными на сервере. Движок без :has() показывает все панели.
Сохранять сразу#
Слово СохранятьСразу — как у тумблера: выбор сам отправляет форму, а с БезПерезагрузки() у формы — в фоне, без перезагрузки страницы. Кнопку сохранения форме всё равно оставьте: без скрипта отправить выбор больше нечем.
Режим уведомлений сохраняется сам
HTML = Кит.Форма("/уведомления",
Кит.Сегменты("notify",
Кит.Сегмент("all", "Все"),
Кит.Сегмент("mentions", "Упоминания"),
Кит.Сегмент("none", "Никаких")).Подпись("Уведомления").Выбрано("mentions").СохранятьСразу(),
Кит.Кнопка("Сохранить").Вторичная()).БезПерезагрузки().Токен("т0кен-сессии").Рендер();Источник значения#
Слово Источник(Текст) — как у поля и тумблера: откуда взялось значение — «по умолчанию · ui.theme», «из окружения». Источник стоит мелко и приглушённо справа на строке подписи группы: подпись группы слева, источник справа, пилюля под ними. Не помещается рядом — уходит под подпись, у правого края. Без группы вокруг источник встаёт своей строкой над пилюлей, справа.
Тема и откуда она взялась
HTML = Кит.Форма("/настройки/тема",
Кит.Группа("Тема",
Кит.Сегменты("theme",
Кит.Сегмент("light", "Светлая"),
Кит.Сегмент("dark", "Тёмная")).Подпись("Тема").Выбрано("dark")
.Источник("по умолчанию · ui.theme")).Вид("простая"),
Кит.Кнопка("Сохранить").Основная()).Токен("т0кен-сессии").Рендер();Группа сегментов ссылается на источник aria-describedby: скринридер дочитает его после имени группы. Без слова разметка сегментов прежняя.
Отказ#
Слово Ошибка(Текст) — как у поля: отказ сервера словами строкой под пилюлей (дев-сборка 10). Чужое значение — старая вкладка, подделанный запрос, вариант, которого больше нет, — сервер отбивает под самими сегментами, а не тостом. Рамка пилюли — цветом ошибки, группа помечена aria-invalid="true" и ссылается на строку отказа aria-describedby; строка — та же field__error с ролью alert, что под полем. У сегментов с наборами полей строка стоит под пилюлей, над набором. Со скриптом после тихой отправки фокус встаёт на отмеченный сегмент отбитой группы. Без слова разметка прежняя.
Сервер отбил чужое значение
HTML = Кит.Форма("/настройки/тема",
Кит.Группа("Пресет темы",
Кит.Сегменты("пресет",
Кит.Сегмент("classic", "Как было"),
Кит.Сегмент("material", "Новая"),
Кит.Сегмент("oscript", "oscript")).Подпись("Пресет темы").Выбрано("material")
.Ошибка("Пресета «sunset» нет — выберите один из трёх.")).Вид("простая"),
Кит.Кнопка("Сохранить").Основная()).Токен("т0кен-сессии").Рендер();Малые#
Малые() — та же пилюля ниже и плотнее: для строки списка, записи реестра, ячейки таблицы, где обычные сегменты выше соседей.
Роль участника в строке таблицы
| Участник | Роль |
|---|---|
| anna | |
| boris |
Участники = Новый Массив();
Для Каждого Пара Из СтрРазделить("anna:write,boris:read", ",") Цикл
Имя = СтрРазделить(Пара, ":")[0];
Участники.Добавить(Новый Структура("name, role", Имя,
Кит.Форма("/участники/" + Имя,
Кит.Сегменты("role",
Кит.Сегмент("read", "Чтение"),
Кит.Сегмент("write", "Запись")).Подпись("Роль: " + Имя)
.Выбрано(СтрРазделить(Пара, ":")[1]).Малые().СохранятьСразу())
.БезПерезагрузки().Токен("т0кен-сессии")));
КонецЦикла;
Таблица = Кит.Таблица(Участники);
Таблица.Колонка("name", "Участник");
Таблица.Колонка("role", "Роль");
HTML = Таблица.Рендер();На телефоне#
На узком экране (до 600 px) подпись сегмента не обрезается многоточием, а переносится во вторую строку: пилюля становится выше, но каждый вариант читается целиком.
Три подписи в два слова — сузьте окно до телефона
HTML = Кит.Сегменты("scheme",
Кит.Сегмент("system", "Как в системе"),
Кит.Сегмент("light", "Всегда светлая"),
Кит.Сегмент("dark", "Всегда тёмная")).Подпись("Тема").Выбрано("system").Рендер();Переносится подпись только между словами. Колонки на телефоне равные, пока слова в них помещаются; колонка не бывает уже самого длинного слова своей подписи — у выбранного сегмента вместе с галкой. Однословная подпись, которой не хватает равной доли, делает свою колонку шире соседей, а остаток ширины делится поровну: «Вручную» не станет «Вручну / ю».
Однословные подписи рядом с длинной — слово не рвётся
HTML = Кит.Сегменты("source",
Кит.Сегмент("manual", "Вручную"),
Кит.Сегмент("github", "GitHub"),
Кит.Сегмент("regex", "Регулярное выражение")).Подпись("Откуда версия").Выбрано("manual").Рендер();Слово не рвётся и тогда, когда места на все слова не хватает: если самые длинные слова всех подписей вместе шире экрана, пилюля выходит за край. Четыре сегмента с длинными однословными подписями телефону не по силам — сократите подписи или дайте выбор полем .Вариант(…).
В окне#
Окно уже экрана: базовое — 560 px, на телефоне — 358. Сегменты в окне на любой ширине держатся так же, как на телефоне (дев-сборка 8): пилюля во всю ширину окна, колонка не уже самого длинного слова, подпись переносится между словами, а не обрезается многоточием.
Пять сегментов в окне — подписи целиком
Окно = Кит.Окно("Оформление метки",
Кит.Сегменты("style",
Кит.Сегмент("stable", "Стабильный"),
Кит.Сегмент("deprecated", "Устаревший"),
Кит.Сегмент("preview", "Предварительный"),
Кит.Сегмент("danger", "Опасный"),
Кит.Сегмент("plain", "Обычный")).Подпись("Стиль метки").Выбрано("stable"))
.Якорь("oformlenie-metki");
HTML = Кит.Холст(Кит.Кнопка("Оформление").Открывает(Окно), Окно).Рендер();Без скрипта#
Сегменты — радиокнопки: выбор, стрелки и отправка с формой работают без скрипта, наборы переключает лист. Скрипт нужен только слову СохранятьСразу — без него выбор уходит кнопкой сохранения, как любое поле.
Гайдлайны#
Код
HTML = Кит.Сегменты("view",
Кит.Сегмент("list", "Списком"),
Кит.Сегмент("cards", "Карточками")).Подпись("Вид списка").Рендер();Код
HTML = Кит.Сегменты("day",
Кит.Сегмент("1", "Понедельник"), Кит.Сегмент("2", "Вторник"), Кит.Сегмент("3", "Среда"),
Кит.Сегмент("4", "Четверг"), Кит.Сегмент("5", "Пятница"), Кит.Сегмент("6", "Суббота"),
Кит.Сегмент("7", "Воскресенье")).Подпись("День").Рендер();Длинный перечень — это поле выбора: Кит.Поле("day", "День").Вариант(…).
Код
HTML = Кит.Сегменты("auth",
Кит.Сегмент("key", "Ключ", Кит.Поле("token", "Ключ доступа")),
Кит.Сегмент("password", "Пароль", Кит.Поле("secret", "Пароль").Секрет())).Подпись("Способ входа").Рендер();Код
HTML = Кит.Сегменты("auth",
Кит.Сегмент("key", "Ключ", Кит.Поле("token", "Ключ доступа").Обязательное()),
Кит.Сегмент("password", "Пароль", Кит.Поле("secret", "Пароль").Секрет().Обязательное()))
.Подпись("Способ входа").Рендер();Браузер не отправит форму, пока пусто обязательное поле, — даже спрятанное в невыбранном наборе, и показать отказ ему негде. Поле набора, без которого нельзя, проверяет сервер для того способа, что пришёл в имени сегментов.
Сводка проекта.
Изменения проекта.
Код
HTML = Кит.Сегменты("tab",
Кит.Сегмент("overview", "Обзор", Кит.Примечание("Сводка проекта.")),
Кит.Сегмент("history", "История", Кит.Примечание("Изменения проекта."))).Рендер();Доступность#
- Группа —
role="radiogroup"с именем из словаПодпись(aria-label): скринридер читает «Тема, группа переключателей, Тёмная, 2 из 3». Без подписи группу называет только соседний текст — дайте её всегда, когда сегменты стоят без видимого заголовка. - Сегмент — настоящая радиокнопка внутри своей метки: Tab приходит в группу один раз, стрелки меняют выбор, пробел отмечает. Фокус виден обводкой сегмента.
- Источник значения связан с группой
aria-describedby: скринридер читает его после имени группы, а не отдельной строкой где-то рядом. - Отказ (
Ошибка) — группаaria-invalid="true", строка отказа с рольюalertсвязана с группойaria-describedby(вместе с источником, если он есть). - Галка выбранного — оформление (
aria-hidden): что отмечено, скринридер узнаёт от радиокнопки. - Спрятанный набор —
display: none: его поля уходят из порядка Tab и из речи скринридера. - Настройка «меньше движения» гасит переходы заливки и въезд галки.
Модель#
Кит.Сегменты()src/Классы/холст/Кит.os
Сегменты — выбор одного из немногих (пресет, тема, режим) рядом кнопок в одной пилюле: выбранный сегмент залит, на нём галка. Под капотом — радиокнопки одного имени: выбор уходит с формой без скрипта, стрелки меняют его движком браузера. Дети — сегменты (Кит.Сегмент); дети сегмента — его набор полей: под группой виден набор отмеченного сегмента, и переключает наборы лист (:has(:checked)), без скрипта. Отмечен ровно один: сегмент со словом Выбран(), иначе совпавший с Выбрано(Значение), иначе первый. Слова: Выбрано(Значение), Подпись (имя группы для скринридера), Малые (низкие — для строки списка), СохранятьСразу (выбор сам отправляет форму, как у тумблера), Источник(Текст) (откуда значение — мелко справа на строке подписи группы, как у поля), Якорь. Параметры: Имя - Строка - имя поля (атрибут name радиокнопок): под ним выбор уходит с формой. Р1…Р8 - ЭлементЭкрана - (необязательные) сегменты, узлы Кит.Сегмент. Возвращаемое значение: ЭлементЭкрана
Кит.Сегмент()src/Классы/холст/Кит.os
Сегмент — один вариант Кит.Сегменты: значение уходит с формой, подпись видна на сегменте. Дети — набор полей, который виден, пока сегмент выбран. Печатается только внутри Кит.Сегменты. Слова: Выбран() — этот сегмент отмечен; Иконка(Имя) — значок перед подписью, глиф или «бренд:<ключ>» (дев-сборка 10); Когда(Ложь) снимает сегмент вместе с его набором. Параметры: Значение - Строка - что уходит с формой, когда сегмент выбран. Подпись - Строка - надпись сегмента, одно-два слова. Р1…Р7 - ЭлементЭкрана, Строка - (необязательные) набор полей сегмента; строка — текст. Возвращаемое значение: ЭлементЭкрана
Сегменты.Отрисовать()src/Классы/холст/Сегменты.os
Печатает сегменты: группу радиокнопок в пилюле и, если у сегментов есть наборы полей,
панели этих наборов под ней. Без сегментов не печатает ничего.
Параметры:
Модель - Структура - Атрибуты (ДОВЕРЕННАЯ строка атрибутов группы: класс, id, data-*),
Имя (атрибут name радиокнопок), Подпись (имя группы для скринридера,
aria-label), Выбрано (значение выбранного сегмента), СохранятьСразу (выбор сам
отправляет форму), Сегменты (массив структур: Значение, Подпись, Выбран —
Булево, Набор — ДОВЕРЕННЫЙ HTML набора полей сегмента или "", Иконка —
необязательно, дев-сборка 10: значок перед подписью, глиф или «бренд:<ключ>»), Источник
(необязательно: откуда значение — «по умолчанию · ui.theme»; печатается перед
группой и стоит справа на строке подписи группы), ИдИсточника (id источника:
на него ссылается aria-describedby группы), Ошибка (необязательно, дев-сборка
10: отказ сервера словами — строкой под пилюлей, как отказ под полем; группа
aria-invalid="true"), ИдОшибки (id строки отказа: на него ссылается
aria-describedby группы).
Возвращаемое значение:
Строка - <div class="segments" role="radiogroup"> либо обёртка .segments-set с группой
и панелями наборов; "" без сегментов.Кит.ПанельСегмента()src/Классы/холст/Кит.os
Панель сегмента вне его набора (0.13, дев-сборка 7): область, которая видна, пока в той же форме отмечен сегмент номер Номер у сегментов с именем ИмяСегментов. Наборы под сегментами (дети Кит.Сегмент) стоят прямо под группой, а панель кладут куда угодно в той же форме — в другой шаг мастера, в другую группу. Переключает её лист кита тем же приёмом, что наборы (:has(:checked), до восьми сегментов), без скрипта; движок без :has() показывает все панели. Поля спрятанной панели уходят с формой, как поля спрятанного набора: лишние отбрасывает сервер. Короче — словом самих сегментов: Сегменты.Панель(Номер, д…). Параметры: ИмяСегментов - Строка - имя Кит.Сегменты той же формы (их атрибут name). Номер - Число, Строка - номер сегмента с 1 или значение сегмента. Р1…Р7 - ЭлементЭкрана, Строка - (необязательные) содержимое панели; строка — текст. Возвращаемое значение: ЭлементЭкрана
ЭлементЭкрана.Панель()src/Классы/холст/ЭлементЭкрана.os
Панель сегмента вне его набора: область, видная, пока в той же форме отмечен сегмент номер Номер этих сегментов, — то же, что Кит.ПанельСегмента(Имя сегментов, Номер, …). Узел панели возвращается: его кладут в другой шаг или группу той же формы (0.13, дев-сборка 7). Параметры: Номер - Число, Строка - номер сегмента с 1 или значение сегмента. Р1…Р7 - ЭлементЭкрана, Строка - (необязательные) содержимое панели; строка — текст. Возвращаемое значение: ЭлементЭкрана - новый узел панели (не сами сегменты).