oscript-ui
oscript-ui

Сетка и интервалы

Интервалы кита идут шагом 4dp, как в Material: восемь ступеней от 4 до 64 пикселей. Равнозначные блоки в ряд раскладывает компонент «Сетка», а зазор между соседями всегда задаёт их контейнер.

Шаг 4dp#

Любой отступ и зазор в ките — одна из восьми ступеней. Между ними нет промежуточных значений, поэтому расстояния на экране повторяются и складываются в ритм.

Токен Пиксели Где в ките
--space-1 4 Между полем и пояснением под ним, между плашками в ряду
--space-2 8 Между кнопками в действиях карточки, плотная сетка
--space-3 12 Между полями формы, поля шапки
--space-4 16 Сетка, ритм тела карточки и секции настроек
--space-5 20 Внутренний отступ карточки и секции, боковые поля страницы
--space-6 32 Над заголовком раздела, над содержимым страницы
--space-7 48 Отступ пустого состояния сверху и снизу
--space-8 64 Под содержимым страницы

Восемь ступеней шага

--space-1
--space-2
--space-3
--space-4
--space-5
--space-6
--space-7
--space-8
Строки = ""; Для Номер = 1 По 8 Цикл Строки = Строки + СтрШаблон("<div style=""display:flex;align-items:center;gap:var(--space-3)"">" + "<code style=""flex:none;width:6em"">--space-%1</code>" + "<span style=""display:block;height:16px;width:var(--space-%1);" + "background:var(--md-sys-color-primary);border-radius:var(--md-sys-shape-corner-extra-small)"">" + "</span></div>", Номер); КонецЦикла; HTML = "<div style=""display:flex;flex-direction:column;gap:var(--space-2)"">" + Строки + "</div>";

Ширина содержимого страницы — --layout-width, 1200 пикселей. Боковые поля — --space-5.

Плотность#

Поле Плотность в оформлении умножает все ступени шага. Допустимы значения от 0,5 до 2, всё остальное заменяется единицей. Посетитель хранит плотность в cookie оформления рядом с темой и акцентом.

Плотность меняет отступы и зазоры, но не высоту управления: кнопка остаётся 36 пикселей, поле — 44. Так экран сжимается, а попадать пальцем и курсором так же легко.

И при единице кит плотнее спецификации Material: он рисует рабочие экраны — списки, формы, настройки, — и на экран должно входить столько же, сколько в прежнем ките 0.9.

Что Было, по спецификации Кит
Текст страницы, body-large 16 / 24 15 / 22
Заголовок карточки и секции, title-large 22 20
Отступ карточки и секции, --space-5 24 20
Поле ввода 56 44, подпись наверху — 12
Кнопка, кнопка-значок 40 36
Малая кнопка 32 28
Ход в шапке, пункт колонки разделов 40 и 48 36
Чип 32 28
Плашка 24 22
Ряд таблицы ≈ 45, шапка 56, ячейка 12 / 16 ≈ 40, шапка 40, ячейка 10 / 12
Карточка объекта, отступ 16 / 24 12 / 16

На телефоне пункт колонки разделов дома настроек остаётся 44 пикселя — под палец.

ТокеныТемы.ПоУмолчанию()src/Классы/оформление/ТокеныТемы.os

Оформление по умолчанию.

Возвращаемое значение:
 Структура - Тема, Акцент, Плотность, Радиус.

Компонент «Сетка»#

Сетка раскладывает равнозначные блоки — карточки, плитки, кнопки — в ряд с переносом. Ячейки приходят готовой разметкой, сама сетка отвечает только за раскладку. Зазор между ячейками — --space-4.

Без поля Колонки ячейка занимает столько, сколько нужно её содержимому, и ряд переносится, когда место кончилось. Так раскладываются плитки сводки.

Авто — плитки сводки

18проектов
7проектов с источниками
Ячейки = Новый Массив(); Ячейки.Добавить(Набор.Плитка().Отрисовать(Новый Структура("Значение, Подпись, Адрес", "1 204", "документов", "/документы"))); Ячейки.Добавить(Набор.Плитка().Отрисовать(Новый Структура("Значение, Подпись", "18", "проектов"))); Ячейки.Добавить(Набор.Плитка().Отрисовать(Новый Структура("Значение, Подпись", "7", "проектов с источниками"))); HTML = Набор.Сетка().Отрисовать(Новый Структура("Ячейки, Заголовок", Ячейки, "Сводка"));

В дереве холста сетка собирается словами (см. Язык узлов): ячейки — дети узла, поля модели — слова Колонки, Плотная, ПолныеРяды, Заголовок.

Узлом холста — словами

18проектов
7проектов с источниками
HTML = Кит.Сетка( Кит.Плитка("1 204", "документов").Адрес("/документы"), Кит.Плитка("18", "проектов"), Кит.Плитка("7", "проектов с источниками")).Колонки(3).Заголовок("Сводка").Рендер();

Колонки#

Колонки — от 1 до 4. Колонки — настоящая CSS-сетка: ширину ячейки задаёт колонка, и она одинакова в любом ряду. У сеток в 3 и 4 колонки число — потолок: шире их не станет ни на каком мониторе, а на узком экране колонок меньше по точкам перелома. Поэтому шесть карточек в трёх колонках всегда заполняют ряды — 3 + 3, 2 + 2 + 2 или по одной, — и хвоста 4 + 2 не бывает.

Колонки Для чего Как ведут себя
1 Стопка блоков Один столбец
2 Два блока содержимого рядом: текст и текст, список и список Колонка не уже 360, пара занимает всю ширину
3 Сетка карточек Три колонки одной ширины, даже если карточек меньше трёх; окно до 900 пикселей — две, до 600 — одна
4 Сетка плиток и мелких карточек Четыре колонки; окно до 1000 пикселей — три, до 760 — две, до 520 — одна

Другое значение Колонки сетка не примет: число попадает в класс, поэтому оно сверяется со списком и превращается в «авто».

Две колонки — связи объекта

Ссылается на

отчёт, справочник

Ссылаются сюда

сводка

Ячейки = Новый Массив(); Ячейки.Добавить(Набор.Карточка().Отрисовать(Новый Структура("Заголовок, Тело", "Ссылается на", "<p>отчёт, справочник</p>"))); Ячейки.Добавить(Набор.Карточка().Отрисовать(Новый Структура("Заголовок, Тело", "Ссылаются сюда", "<p>сводка</p>"))); HTML = Набор.Сетка().Отрисовать(Новый Структура("Ячейки, Колонки", Ячейки, 2));

Карточка в ячейке тянется на всю высоту ряда, а полоса действий встаёт у её нижнего края. Описания разной длины не разводят кнопки по разным линиям.

Три колонки — карточки одной высоты, действия на одной линии

Отчёты

Сводки по проектам за неделю.

Источники

Репозитории, из которых собираются пакеты: адрес, ветка, ключ доступа и расписание.

Доступ

Участники и роли.

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

Шесть карточек в трёх колонках ложатся двумя полными рядами; сузьте окно — ряды по две, потом по одной.

Три колонки — шесть карточек заполняют ряды

Отчёты

Раздел проекта.

Источники

Раздел проекта.

Доступ

Раздел проекта.

Ключи

Раздел проекта.

Журнал

Раздел проекта.

Расписание

Раздел проекта.

Ячейки = Новый Массив(); Для Каждого Имя Из СтрРазделить("Отчёты,Источники,Доступ,Ключи,Журнал,Расписание", ",") Цикл Ячейки.Добавить(Набор.Карточка().Отрисовать(Новый Структура("Заголовок, Описание", Имя, "Раздел проекта."))); КонецЦикла; HTML = Набор.Сетка().Отрисовать(Новый Структура("Ячейки, Колонки", Ячейки, 3));

Полные ряды#

В широкой раскладке три колонки дают широкие и низкие карточки — растянутые и сплюснутые. Четыре колонки им к лицу, но шесть карточек в четырёх колонках ложатся 4 + 2, и второй ряд торчит хвостом. Слово ПолныеРяды решает это без скрипта: на каждой ширине карточки неполного последнего ряда не показываются.

Сколько карточек класть, решает экран, и правило простое — число, которое делится на все ряды. Восемь карточек в Колонки(4).ПолныеРяды():

Окно Колонок Видно
шире 1000 4 8 — два ряда по четыре
761–1000 3 6 — два ряда по три, две последние не показываются
521–760 2 8 — четыре ряда по две
до 520 1 8 — столбиком

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

Восемь карточек в четырёх колонках — ряды всегда полные

Отчёты

Раздел проекта.

Источники

Раздел проекта.

Доступ

Раздел проекта.

Ключи

Раздел проекта.

Журнал

Раздел проекта.

Расписание

Раздел проекта.

Сборки

Раздел проекта.

Выпуски

Раздел проекта.

Сетка = Кит.Сетка().Колонки(4).ПолныеРяды().Заголовок("Разделы проекта"); Для Каждого Имя Из СтрРазделить("Отчёты,Источники,Доступ,Ключи,Журнал,Расписание,Сборки,Выпуски", ",") Цикл Сетка.Добавить(Кит.Карточка(Имя).Описание("Раздел проекта.").Адрес("/" + НРег(Имя))); КонецЦикла; HTML = Сетка.Рендер();

Сузьте окно: на средней ширине ряды по три, и седьмой с восьмой карточкой нет; ещё уже — снова все восемь, по две и по одной.

Как это устроено. Число карточек лист не знает, поэтому каждая ячейка находит себя сама: :nth-child — её место в ряду, :nth-last-child — сколько ячеек после неё. Первая ячейка ряда, после которой меньше ячеек, чем колонок, — начало хвоста, и она прячется (display: none) вместе с остальными ячейками хвоста. Правила стоят на тех же точках перелома, что и потолок колонок, и каждое действует только на своём отрезке ширин. Первый ряд не прячется никогда: три карточки в четырёх колонках остаются тремя, а не пустой сеткой.

Слово — только у сеток в 3 и 4 колонки. У двух колонок число рядов зависит от ширины ячейки (auto-fit), а не от точек перелома, и лист не знает, какой ряд неполный: ПолныеРяды у Колонки(1), Колонки(2) и «авто» останавливает сборку.

Секция со списком карточек на телефоне#

На телефоне (до 600 px) карточка или секция, в которой лежит сетка карточек объектов, становится плоской: без кольца, заливки и боковых полей, заголовок и пояснение — на поле страницы. Карточки объектов в ней остаются карточками, как на широком экране, во всю ширину экрана без полей страницы — на 390 это 358 пикселей. Таблица и одиночная карточка внутри карточки — частью родителя, как на странице Карточка.

Секция со списком карточек на телефоне — сузьте окно

Недавно обновлённые

Свежие версии проектов, к которым у вас есть доступ.

отчёты

новый

Сводки за неделю для бухгалтерии.

Выгрузка данных во внешние системы.

Ячейки = Новый Массив(); Для Каждого Пара Из СтрРазделить("отчёты:Сводки за неделю для бухгалтерии.,справочники:Общие справочники всех проектов.," + "выгрузка:Выгрузка данных во внешние системы.", ",") Цикл Имя = СтрРазделить(Пара, ":")[0]; Ячейки.Добавить(Кит.Статья( Кит.Область( Кит.Заголовок(3, Кит.Иконка("пакет"), Кит.Ссылка("/проекты/" + Имя, Имя)).Вид("имя"), Кит.Область(Кит.Плашка("новый").Вид("accent")).Вид("плашки")).Вид("шапка"), Кит.Примечание(СтрРазделить(Пара, ":")[1]).Вид("описание")).Вид("карточка")); КонецЦикла; HTML = Кит.Карточка("Недавно обновлённые", Кит.Сетка(Ячейки[0], Ячейки[1], Ячейки[2]).Колонки(3)) .Иконка("история").Описание("Свежие версии проектов, к которым у вас есть доступ.").Рендер();

Плотная сетка#

Плотная уменьшает зазор до --space-2. Она нужна мелким однородным блокам — кнопкам, чипам, — которым обычный зазор велик.

Плотная — кнопки входа

Ячейки = Новый Массив(); Ячейки.Добавить(Набор.Кнопка().Отрисовать(Новый Структура("Подпись, Вариант, Адрес", "Войти через провайдера 1", "вторичная", "/вход/провайдер-1"))); Ячейки.Добавить(Набор.Кнопка().Отрисовать(Новый Структура("Подпись, Вариант, Адрес", "Войти через провайдера 2", "вторичная", "/вход/провайдер-2"))); HTML = Набор.Сетка().Отрисовать(Новый Структура("Ячейки, Плотная", Ячейки, Истина));

Пустая сетка#

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

Пустая — ничего

этот вызов не печатает ничего

HTML = Набор.Сетка().Отрисовать(Новый Структура("Ячейки", Новый Массив()));

Узлом холста#

На холсте сетка — узел, и каждый её ребёнок становится своей ячейкой. Сетка в общем ряду раскладок описана на странице раскладки.

Узлом дерева — ячейки кладут детьми

слева

справа

HTML = Кит.Сетка( Кит.Примечание("слева"), Кит.Примечание("справа")).Колонки(2).Рендер();

Интервалом владеет контейнер#

У поля, тумблера и других блоков нет собственного внешнего отступа. Расстояние между соседями задаёт тот, кто их держит:

  • форма столбиком (.form-stack) — зазором --space-3;
  • тело карточки и тело секции настроек — отступом --space-4 над каждым соседом, кроме первого;
  • сетка — зазором --space-4 или --space-2.

Поля в карточке — ритм даёт тело карточки

Проект

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

Почему не поле: отступ поля складывался с зазором контейнера, и формы дышали двойным ритмом 16 + 12. В строке отбора нижний отступ поля ещё и поднимал его над кнопкой рядом. Один владелец интервала лечит и то, и другое.

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

.report-filters {
	display: flex;
	flex-direction: column;
	gap: var(--space-3);
}

Без display: flex или grid свойство gap не работает — правило выглядит рабочим и ничего не делает.

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

Гайдлайны#

ДелайтеПоля в контейнере с ритмом: зазор одинаковый
Код
HTML = "<div class=""form-stack"">"
	+ Набор.Поле().Отрисовать(Новый Структура("Имя, Метка", "имя", "Имя"))
	+ Набор.Поле().Отрисовать(Новый Структура("Имя, Метка", "почта", "Почта"))
	+ "</div>";
Не делайтеПоля в голом блоке: своего отступа у поля нет, и они слипаются
Код
HTML = "<div>"
	+ Набор.Поле().Отрисовать(Новый Структура("Имя, Метка", "имя", "Имя"))
	+ Набор.Поле().Отрисовать(Новый Структура("Имя, Метка", "почта", "Почта"))
	+ "</div>";
ДелайтеЗазор — ступенью шага
Код
HTML = "<div style=""display:flex;gap:var(--space-2)"">"
	+ Набор.Кнопка().Отрисовать(Новый Структура("Подпись, Вариант", "Назад", "вторичная"))
	+ Набор.Кнопка().Отрисовать(Новый Структура("Подпись, Вариант", "Пропустить", "вторичная"))
	+ Набор.Кнопка().Отрисовать("Далее")
	+ "</div>";
Не делайтеЗазоры литералом: 3 и 13 пикселей рядом читаются как разные группы
Код
HTML = "<div style=""display:flex"">"
	+ Набор.Кнопка().Отрисовать(Новый Структура("Подпись, Вариант", "Назад", "вторичная"))
	+ "<span style=""width:3px""></span>"
	+ Набор.Кнопка().Отрисовать(Новый Структура("Подпись, Вариант", "Пропустить", "вторичная"))
	+ "<span style=""width:13px""></span>"
	+ Набор.Кнопка().Отрисовать("Далее")
	+ "</div>";

Отчёты

Сводки за неделю.

Источники

Репозитории, ветки, ключи доступа и расписание сборки.

ДелайтеКарточки в ряд — сеткой: одна высота, действия на одной линии
Код
Ячейки = Новый Массив();
Ячейки.Добавить(Набор.Карточка().Отрисовать(Новый Структура("Заголовок, Описание, Действия",
	"Отчёты", "Сводки за неделю.", Набор.Кнопка().Отрисовать(Новый Структура("Подпись, Вариант", "Открыть", "ссылка")))));
Ячейки.Добавить(Набор.Карточка().Отрисовать(Новый Структура("Заголовок, Описание, Действия",
	"Источники", "Репозитории, ветки, ключи доступа и расписание сборки.",
	Набор.Кнопка().Отрисовать(Новый Структура("Подпись, Вариант", "Открыть", "ссылка")))));

HTML = Набор.Сетка().Отрисовать(Новый Структура("Ячейки, Колонки", Ячейки, 2));

Отчёты

Сводки за неделю.

Источники

Репозитории, ветки, ключи доступа и расписание сборки.

Не делайтеКарточки в ряд своим флексом: высоты и кнопки разъезжаются
Код
HTML = "<div style=""display:flex;align-items:flex-start;gap:var(--space-4)"">"
	+ Набор.Карточка().Отрисовать(Новый Структура("Заголовок, Описание, Действия",
		"Отчёты", "Сводки за неделю.", Набор.Кнопка().Отрисовать(Новый Структура("Подпись, Вариант", "Открыть", "ссылка"))))
	+ Набор.Карточка().Отрисовать(Новый Структура("Заголовок, Описание, Действия",
		"Источники", "Репозитории, ветки, ключи доступа и расписание сборки.",
		Набор.Кнопка().Отрисовать(Новый Структура("Подпись, Вариант", "Открыть", "ссылка"))))
	+ "</div>";

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

  • Заголовок сетки печатается aria-label: скринридер назовёт группу блоков («Сводка»), прежде чем читать их.
  • Порядок ячеек в разметке — порядок чтения. Сетка его не переставляет: на узком экране блоки идут столбцом в том же порядке.
  • Плотность ниже единицы сжимает зазоры, но не цели нажатия: высота кнопки и поля от неё не зависит.
  • Карточка, которую ПолныеРяды не показывает, скрыта и от скринридера (display: none). Сетка с полными рядами — витрина, а не единственный путь: всё, что в ней может не показаться, должно быть доступно и по ссылке «Все…» под ней.

Модель#

Структура ниже — прежний вид модели: её получает набор, а в дереве холста те же поля задают слова Колонки, Плотная, ПолныеРяды и Заголовок.

Сетка.Отрисовать()src/Классы/компоненты/Сетка.os

Отрисовывает сетку равнозначных блоков.

Параметры:
 Модель - Структура - Ячейки (массив: ДОВЕРЕННЫЙ HTML строкой либо структура с полем
          HTML; пустые пропускаются), Колонки ("авто" | 1 | 2 | 3 | 4; 1 — стопка
          в один столбец), Заголовок (aria-label области), Плотная (уменьшенный зазор),
          ПолныеРяды (только у 3 и 4 колонок: ячейки неполного последнего ряда на каждой
          ширине не показываются — ряды всегда полные).

Возвращаемое значение:
 Строка - фрагмент <div class="grid"> либо "" для пустого набора ячеек.

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

Сетка равнозначных блоков: КАЖДЫЙ ребёнок узла становится своей ячейкой. Слова:
Колонки, Плотная, ПолныеРяды, Заголовок.

Параметры:
 Р1…Р8 - ЭлементЭкрана, Структура - (необязательные) ячейки сетки; Структура первым
         аргументом — прежний вид модели без поля Ячейки.

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