oscript-ui
oscript-ui

Холст и узлы

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

Модель#

В модели три участника.

  • Кит — фабрика узлов. Каждая функция кита (Кит.Ряд, Кит.Таблица, Кит.Кнопка) возвращает новый узел.
  • Элемент экрана — сам узел. У него есть род, вид, родитель и дети. Детей передают фабрике аргументами или кладут методом Добавить, печатают методом Рендер.
  • Холст — корень куска экрана. Своего тега не печатает: он только держит детей и печатает их подряд.

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

Список объектов: шапка, таблица, карточки, действие

Витринахолст, ряд, надпись, текст
Имя
отчёт

объект

описание объекта

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

Дети аргументами#

Фабрика контейнера принимает до восьми детей позиционно, по порядку на экране. Неопределено пропускается: ребёнок, которого может не быть, пишется прямо в списке. Девятый и дальше кладутся методом Добавить; Добавить(Неопределено) тоже ничего не кладёт — необязательную часть экрана можно добавлять в цикле, не оборачивая её в Кит.Холст.

Строка на месте узла — текст: она становится узлом Кит.Текст и экранируется, разметкой не станет никогда.

Строки — текст, узлы — узлы

<p class="muted">Ключ <code>api-7f2c</code> показывается один раз.</p>
HTML = Кит.Примечание("Ключ ", Кит.Код("api-7f2c"), " показывается один раз.").Рендер();

Добавить возвращает положенное#

Добавить возвращает не родителя, а ребёнка — тот узел, который только что положили. Поэтому цепочка Холст.Добавить(Кит.Ряд(...)).Добавить(...) спускается вглубь дерева, а не наполняет один уровень. Строку Добавить тоже принимает и отдаёт её текстовый узел.

Нужен родитель — возьмите его методом Родитель(). Так узел, собранный в одну строку, отдают целиком.

Цепочка вглубь и возврат к родителю

Ключ показывается один раз.

Абзац = Кит.Примечание().Добавить("Ключ показывается один раз.").Родитель(); HTML = Абзац.Рендер();

Список, который собирается циклом, наполняют Добавить:

Дети из цикла — Добавить

свежийпровереноткрытый
Ряд = Кит.Ряд("признаки"); Для Каждого Имя Из СтрРазделить("свежий,проверен,открытый", ",") Цикл Ряд.Добавить(Кит.Плашка(Имя)); КонецЦикла; HTML = Ряд.Рендер();

Род и вид#

Род — что это за узел: ряд, таблица, ссылка, текст. Род определяет тег и то, что можно положить внутрь. Вид — оформление узла из закрытого набора его рода. Вид задают словом .Вид(…): Кит.Таблица(…).Вид("карточками"). У ряда вид — его смысл, поэтому он первым аргументом: Кит.Ряд("выбор", …).

Вид — не имя CSS-класса. Его проверяют сразу: опечатка останавливает сборку, а не даёт молча голый экран. Все роды и их виды собраны в словаре родов.

Неизвестный вид — исключение

Разметка элементов: узел «ряд» вида «кнопки» не знает. Вид — значение из закрытого набора кита, а не имя CSS-класса; этот род знает виды: выбор, признаки, свёрнутые, плашки, справа, строкой, действия (пусто — рода «ряд» кит не знает вовсе, «—» значит «без вида»).

Попытка Кит.Ряд("кнопки"); Исключение HTML = Кит.Примечание(КраткоеПредставлениеОшибки(ИнформацияОбОшибке())).Рендер(); КонецПопытки;

Прежде вид шёл вторым аргументом: Кит.Примечание("Ничего", "пусто"). Теперь там ребёнок-текст, и слово «пусто» напечаталось бы на экране. Строку, совпавшую с видом рода, кит ловит при рождении узла и подсказывает слово .Вид(…).

Условие словом#

.Когда(Условие) есть у любого узла: ложь печатает пустую строку, а узел остаётся в дереве — Дерево() показывает его пометкой [скрыт]. Условие вычисляется всегда; там, где вычислять дорого, пишите Если и Кит.Ничего().

Когда — узел на месте, на экране его нет

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

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

ЕстьКлючи = Ложь; HTML = Кит.Холст( Кит.Заголовок(3, "Ключи доступа"), Кит.Примечание("Ключей пока нет.").Вид("пусто").Когда(НЕ ЕстьКлючи), Кит.Кнопка("Отозвать все").Опасная().Когда(ЕстьКлючи)).Рендер();

Правила вложения#

Дерево проверяет себя на каждом ребёнке. Ошибку видно сразу, в строке, где её сделали, а не на экране, с которого содержимое молча пропало.

  • Внутрь кладут узел или строку текста. Готовую разметку, которую пока некому собрать деревом, заворачивают в Кит.Готовое().
  • Лист дерева (текст, картинка, мета, компонент без слота) детей не принимает.
  • Один узел — одно место на экране. Для второго места возьмите у кита новый узел.
  • Узел нельзя положить в самого себя или в своего потомка.
  • Некоторые роды принимают только своих детей: в таблицу кладут колонки и записи, в запись — ячейки, в список — пункты, в свойства — свойства, в документ — голову и тело. Строка в таком роде — тоже чужой ребёнок.
  • В форму строка не кладётся вовсе: токен задаётся словом .Токен(…), текст — узлом Кит.Текст().

Чужой ребёнок — исключение

Разметка элементов: внутрь узла «список» кладут только «пункт», а не «примечание»: узел чужого рода этот родитель не печатает, и содержимое молча исчезло бы с экрана.

Попытка Кит.Список(Кит.Примечание("абзац вместо пункта")); Исключение HTML = Кит.Примечание(КраткоеПредставлениеОшибки(ИнформацияОбОшибке())).Рендер(); КонецПопытки;

Строка в записи таблицы — исключение

Разметка элементов: внутрь узла «запись» кладут только «ячейка», а не «текст»: узел чужого рода этот родитель не печатает, и содержимое молча исчезло бы с экрана.

Попытка Кит.Запись("значение без ячейки"); Исключение HTML = Кит.Примечание(КраткоеПредставлениеОшибки(ИнформацияОбОшибке())).Рендер(); КонецПопытки;

Холст и Ничего#

Кит.Холст() и Кит.Ничего() возвращают одинаковый узел и печатаются одинаково — пустой строкой, пока в них ничего не положили. Разница видна только тому, кто читает код экрана, и в ней весь смысл.

  • Кит.Холст(…) — корень куска экрана: его наполняют и печатают целиком.
  • Кит.Ничего() — место, где показывать нечего: его не наполняют, он и есть ответ.

Ничего печатается пустой строкой

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

HTML = Кит.Ничего().Рендер();

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

Функция БлокЧерновика(Знач Статья)

	Если Статья.Черновик Тогда
		Возврат Кит.Ничего();   // на странице этого места просто не будет
	КонецЕсли;

	Возврат Кит.Холст(          // а здесь собираем кусок экрана
		Кит.Заголовок(3, "Как читать"),
		Кит.Примечание(Статья.Пояснение).Вид("описание"));

КонецФункции

На Возврат Кит.Ничего(); читатель останавливается: элемента здесь нет. На Возврат Кит.Холст(); он идёт вверх по функции искать, чем холст наполнили, и не находит ничего.

Слово «пусто» в ките уже занято видимым смыслом: компонент «Пустое состояние», вид примечания «пусто» и Таблица.Пусто(). Все три что-то показывают человеку, поэтому место без содержимого называется иначе.

Дерево узла#

Дерево() печатает, из чего собран кусок экрана: род и вид каждого узла, вложенность отступом, снятые словом Когда — пометкой [скрыт]. Это ответ на вопрос «что здесь напечатано» без чтения функций, которые экран собирали.

Из чего собран экран

холст
  ряд[выбор]
    компонент[кнопка]
  таблица
    пусто
      примечание[пусто]
        текст
    колонка
      текст
    запись
      разворот
      ячейка
        код
          текст
  примечание [скрыт]
    текст
Холст = Кит.Холст( Кит.Ряд("выбор", Кит.Кнопка("Все").Вторичная()), Кит.Таблица( Кит.Колонка("Имя"), Кит.Запись(Кит.Ячейка(Кит.Код("отчёт")))).Пусто("Ничего не найдено"), Кит.Примечание("Фильтр не задан.").Когда(Ложь)); HTML = Кит.Код(Холст.Дерево()).Вид("блочный").Рендер();

Свойства узла#

Атрибуты узла задаются словами, а не строкой. Каждое слово возвращает тот же узел, поэтому их ставят цепочкой.

  • Подпись — доступное имя ряда, легенда группы, заголовок таблицы, имя свойства, текст alt картинки.
  • Якорь — атрибут id: на него ссылаются адрес с решёткой и кнопка вне своей формы.
  • Признак — атрибут data-* для скрипта страницы. Печатается даже пустым. Имя — латиницей, цифрами и дефисом: признак с другим именем молча не печатается.
  • Роль — роль для скринридера из набора кита: «группа», «состояние», «тревога».
  • Подсказка — атрибут title.
  • Отключить и Скрыть — атрибуты disabled и hidden.
  • Вид, Когда, Токен — вид узла, условие показа и CSRF-токен для форм внутри.

У компонента те же слова пишут поля его модели — подробно на странице Компонент в дереве.

Свойства печатаются атрибутами

<div id="шаг-2" class="inset" role="status" title="Шаг второй" data-step="2"><p class="muted">Ключ создан</p></div>
HTML = Кит.Область(Кит.Примечание("Ключ создан")) .Вид("вставка").Якорь("шаг-2").Роль("состояние").Подсказка("Шаг второй") .Признак("step", "2").Рендер();

Отключённая и скрытая группа полей

<fieldset class="field-set" disabled hidden><legend>Расписание</legend><label class="field"><span class="field__label">Время</span><input class="field__control" type="text" name="время"></label></fieldset>
HTML = Кит.Группа("Расписание", Кит.Поле("время", "Время")) .Вид("простая").Отключить().Скрыть().Рендер();

Гайдлайны#

Отчёт за март

ДелайтеНичего() там, где показывать нечего: отсутствие видно в коде сразу
Код
Черновик = Истина;
Холст = Кит.Холст(Кит.Заголовок(3, "Отчёт за март"));
Если Черновик Тогда
	Холст.Добавить(Кит.Ничего());
Иначе
	Холст.Добавить(Кит.Примечание("Как читать отчёт").Вид("описание"));
КонецЕсли;
HTML = Холст.Рендер();

Отчёт за март

Не делайтеПустой Холст() вместо Ничего(): читатель ищет, чем его наполнили
Код
Черновик = Истина;
Холст = Кит.Холст(Кит.Заголовок(3, "Отчёт за март"));
Если Черновик Тогда
	Холст.Добавить(Кит.Холст());
Иначе
	Холст.Добавить(Кит.Примечание("Как читать отчёт").Вид("описание"));
КонецЕсли;
HTML = Холст.Рендер();

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

Ключ показывается один раз.

ДелайтеОдин корень и один Рендер() в конце
Код
HTML = Кит.Холст(
	Кит.Заголовок(3, "Ключи доступа"),
	Кит.Примечание("Ключ показывается один раз.")).Рендер();

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

Ключ показывается один раз.

Не делайтеСклейка напечатанных кусков: дерево экрана теряется
Код
HTML = Кит.Заголовок(3, "Ключи доступа").Рендер()
	+ Кит.Примечание("Ключ показывается один раз.").Рендер();

Имя <main> уже занято.

ДелайтеТекст — строкой-ребёнком: она экранируется сама
Код
HTML = Кит.Примечание("Имя ", Кит.Код("<main>"), " уже занято.").Рендер();

Имя <main> уже занято.

Не делайтеРазметка строкой через Готовое(): экранировать её некому
Код
HTML = Кит.Примечание(Кит.Готовое("Имя <code>&lt;main&gt;</code> уже занято.")).Рендер();

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

  • Текст из данных печатается текстом — строкой-ребёнком или узлом Кит.Текст() — и экранируется всегда: разметкой он не станет.
  • Уровень заголовка — число от 1 до 6, другой кит не соберёт. Не пропускайте уровни: скринридер строит по ним оглавление страницы.
  • Ряд с подписью получает aria-label (ставьте ему и роль «группа», иначе имя у div не прочтут), группа — видимую легенду, картинка — alt. Картинка без подписи декоративна, скринридер её пропускает.
  • Роль ставится только из набора кита, произвольную строку кит отвергнет.
  • Пустые ссылка, заголовок и примечание не печатаются вовсе: безымянная ссылка и пустой заголовок для скринридера — дыра.

Справка#

Кит.Холст()src/Классы/холст/Кит.os

Холст — корень куска экрана: его наполняют и печатают целиком. Своего тега не печатает:
обёртка, которой на экране никто не просил, ломает вертикальный ритм соседей. Место,
где показывать нечего, — это Ничего(), а не холст, который забыли наполнить.

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

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

Кит.Ничего()src/Классы/холст/Кит.os

Ничего — место экрана, где показывать нечего: всюду, где принимают холст, но не в обход
правил вложения — внутрь записи по-прежнему кладут ячейку, внутрь таблицы запись,
внутрь списка пункт. Узел тот же, что холст, и печатается он так же — пустой строкой;
отличается тем, что читатель «Возврат Кит.Ничего();» видит отсутствие элемента сразу,
а не ищет по функции, чем этот холст наполнили. Наполнять его незачем: собираете кусок
экрана — берите Холст(). Именем «пусто» звать нельзя: у ПустоеСостояние, примечания
вида «пусто» и Таблица.Пусто() смысл обратный — они человеку что-то ПОКАЗЫВАЮТ.

Возвращаемое значение:
 ЭлементЭкрана - печатается пустой строкой.

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

Кладёт элемент внутрь этого. Порядок детей и есть порядок на экране. Строка на месте
элемента — это текст: она становится узлом Кит.Текст и экранируется. Сырую разметку
кладут только узлом Кит.Готовое(). Неопределено пропускается, как позиционный ребёнок:
необязательная часть экрана кладётся без обёртки в Кит.Холст (0.13, дев-сборка 7).

Параметры:
 Ребёнок - ЭлементЭкрана, Строка, Неопределено - что положить; Неопределено — ничего.

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

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

Кладёт внутрь до восьми элементов разом, по порядку. Неопределено пропускается: так
условный ребёнок пишется прямо в списке, а девятый и дальше кладутся Добавить().

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

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

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

Элемент, внутри которого лежит этот.

Возвращаемое значение:
 ЭлементЭкрана, Неопределено - родитель; Неопределено у корня.

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

Печатает элемент и всё, что внутри него.

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

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

Из чего собран этот кусок экрана — построчное дерево родов и видов. Отвечает объектом
на вопрос «что здесь напечатано», не заставляя читать одиннадцать файлов.

Возвращаемое значение:
 Строка - строки вида «  ряд[выбор]», вложенность — отступом; узел, снятый словом
          Когда(Ложь), помечен « [скрыт]».

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

Вид узла — значение из закрытого набора его рода; проверяется так же, как при рождении.
У компонента пишет поле Вид его модели.

Параметры:
 Значение - Строка - вид.

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

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

Печатает узел только при условии. Ложь печатает пустую строку, но узел остаётся
в дереве (Дерево() показывает его пометкой [скрыт]); у колонки таблицы ложь снимает
и заголовок, и ячейки под ним. Условие вычисляется всегда: там, где вычислять дорого,
пишите Если и Кит.Ничего().

Параметры:
 Условие - Булево - печатать ли узел.

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

ЭлементЭкрана.Подпись()src/Классы/холст/ЭлементЭкрана.os

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

Параметры:
 Текст - Строка - подпись (экранируется при печати).

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

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

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

Параметры:
 Значение - Строка - идентификатор, уникальный на странице.

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

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

Вешает на элемент data-признак — то, за что его берёт скрипт страницы.

Параметры:
 Имя      - Строка - имя признака БЕЗ префикса data-, латиницей: буква, затем буквы,
            цифры и дефисы («step», «row-id»). Другое имя браузер принял бы не везде,
            поэтому оно останавливает сборку исключением, а не пропадает молча.
 Значение - Строка - значение признака.

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

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

Задаёт роль элемента для скринридера.

Параметры:
 Значение - Строка - роль ARIA («группа»).

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