ui · UI-библиотека 26.675.0 Ломающее

UI 26.675.0: ui.actionpanel переехал в бандл, а rich_text по-новому обходится с квадратными скобками

4 мин чтения Устаревших API: 8

Ломающее обновление

Удалены или изменены публичные API — прикладной код может перестать работать.

Модуль ui 26.675.0 собран 26 августа 2026. Из PHP поменялись Converter и Whitelist в Bitrix\UI\Format\BBCode, и пользовательские поля типа rich_text теперь иначе сохраняют и индексируют текст с квадратными скобками. На фронтенде старую панель групповых действий ui.actionpanel перевели на бандл и посадили на новое расширение ui.action-panel, написанное на TypeScript. Если вы подключаете панель по прямым путям к файлам или вызываете её внутренние методы, после обновления это сломается.

Проверьте, как вы подключаете ui.actionpanel

Файлы старой панели переехали в src/, собираются в dist/actionpanel.bundle.js и dist/actionpanel.bundle.css, и config.php расширения теперь ссылается только на них. Ключи bundle_js/bundle_css со значением ui_actionpanel из конфига убрали, в rel добавились main.core и ui.action-panel.

В снапшоте после обновления по старым путям файлов нет:

  • /bitrix/js/ui/actionpanel/panel.js
  • /bitrix/js/ui/actionpanel/item.js
  • /bitrix/js/ui/actionpanel/css/style.css

Вместе с ними удалены panel.min.js, item.min.js, css/style.min.css и карты panel.map.js, item.map.js. Код, который подключает панель через Asset::addJs() или <script src> по этим путям, получит 404, и BX.UI.ActionPanel на странице не появится. Подключайте расширение по имени, тогда ядро само подтянет бандл и зависимости:

        <?php declare(strict_types=1);

use Bitrix\Main\UI\Extension;

// было: Asset::getInstance()->addJs('/bitrix/js/ui/actionpanel/panel.js');
Extension::load('ui.actionpanel');

    

Не опирайтесь на удалённые методы BX.UI.ActionPanel

Из прототипа легаси-панели пропали пять методов: getMutationObserver(), getMutationObserverParam(), addHiddenItem(), removeHiddenItem() и handleGridUnSelectItem().

Первые два обслуживали MutationObserver, который смотрел за всем document.body с subtree: true и на любую мутацию страницы пересчитывал стиль панели. Теперь позицию ведёт PositionTracker из нового расширения. По описанию в action-panel.bundle.d.ts он следит за цепочкой родителей через ResizeObserver, слушает scroll и resize и заменяет легаси-наблюдатель за всем документом. Собственные подписки панели на scroll и resize окна тоже убраны, трекер держит их один.

Скрытые кнопки, которые не влезли в ряд, теперь считает OverflowCalculator. Метод fillHiddenItems() остался и берёт результат его recalc(). По комментарию к калькулятору, у старой панели было две расходящиеся реализации: обработчик resize считал правильно, а addItems терял блок «Ещё», если были скрытые кнопки.

Если вы наследовались от BX.UI.ActionPanel или подменяли эти методы на экземпляре, код перестанет срабатывать. Ещё поменялось поведение:

  • на одном родительском контейнере теперь живёт одна панель, draw() новой вызывает destroy() у предыдущей. Метод destroy() у легаси-панели появился в этом же релизе;
  • класс ui-action-panel-shown снимается с body, только когда на странице не осталось показанных панелей;
  • обработчик клика вне панели на каждом клике заново вызывает this.handleOuterClick(event). По комментарию в коде, потребители подменяют этот метод на экземпляре, и теперь такая подмена срабатывает.

Проверьте, что поля rich_text делают со скобками

Converter и Whitelist появились в 26.600.0 вместе с типом пользовательского поля rich_text (Bitrix\UI\UserField\Types\RichTextType). Перед сохранением поле пропускает значение через Whitelist::normalize(), а текст для поискового индекса берёт из Converter::toPlainText(). Тот же toPlainText() вызывает шаблон main.public_text компонента ui.field.richtext. В 26.675.0 оба метода стали строже к тому, что считать тегом.

Whitelist::normalize() снимает запрещённые теги и вызывает escapeDanglingBrackets(). Колбэк у последнего прежний. Всё, что совпало с паттерном тега, остаётся как есть, а одиночные [ и ] превращаются в &#91; и &#93;. Поменялся сам паттерн. Раньше тегом считался любой токен, который начинается с имени тега из белого списка как отдельного слова и тянется до закрывающей скобки, теперь только строгие формы [tag], [tag=value], [tag attr=value] и [/tag]. Поэтому [p.s.], [b 2024] и [img of the report] тегами больше не считаются, и при сохранении их скобки экранируются.

Converter::toPlainText() раньше заканчивал цепочку замен регуляркой \[/?[a-z][a-z0-9]*\b[^\]]*\], которая удаляла любой токен в скобках, начинающийся с латинской буквы ([слово] на кириллице она не трогала). Теперь цепочка начинается с запрещённых тегов. Блочные (quote, code, table, tr, td, th, spoiler, align) заменяются пробелом, чтобы соседние блоки не слипались в одно слово, остальные снимает Whitelist::stripForbiddenBbTags(). Медиа-блоки [img]…[/img] и [video]…[/video] по-прежнему вырезаются вместе с содержимым, чтобы поиск не индексировал URL встраивания, но только если открывающий тег записан строго. [list], [p] и [*] заменяются пробелом. Последними из тегов снимаются оставшиеся теги белого списка (b, i, u, s, url и непарные img/video), а остальное в скобках, по комментарию в коде, остаётся как пользовательский текст.

Я собрал обе версии классов (старую из диффа 26.600.0, в 26.650.0 она не менялась, и новую с патчем этого релиза) и прогнал через них несколько строк.

Converter::toPlainText():

Вход 26.650.0 26.675.0
[quote]Цена[/quote][quote]99[/quote] Цена99 Цена 99
Отчёт [p.s.] готов, см. [b 2024] Отчёт готов, см. Отчёт [p.s.] готов, см. [b 2024]
См. [img of the report] ниже и картинку [img]https://bxmax.ru/a.png[/img] См. См. [img of the report] ниже и картинку
[foo]bar[/foo] и [1] пункт bar и [1] пункт [foo]bar[/foo] и [1] пункт
[table][tr][td]a[/td][td]b[/td][/tr][/table] ab a b

Whitelist::normalize():

Вход 26.650.0 26.675.0
Отчёт [p.s.] готов, см. [b 2024] без изменений Отчёт &#91;p.s.&#93; готов, см. &#91;b 2024&#93;
См. [img of the report] ниже и картинку [img]https://bxmax.ru/a.png[/img] без изменений См. &#91;img of the report&#93; ниже и картинку [img]https://bxmax.ru/a.png[/img]

Строка из обычных тегов вроде [b]Жирный[/b] и [color=red]крас[/color]ный в обеих версиях даёт одно и то же: normalize() оставляет [b]Жирный[/b] и красный, toPlainText() возвращает Жирный и красный.

Если ваш код читает сырое значение поля rich_text и ищет в нём квадратные скобки, в новых записях он встретит &#91; и &#93;. Миграции для уже сохранённых значений в снапшоте не видно, так что в базе, скорее всего, будут лежать обе формы. toPlainText() после снятия тегов превращает &#91; и &#93; обратно в скобки, поэтому для сырой и для экранированной строки новая версия выдаёт одинаковый текст. В поисковый индекс поля теперь попадает больше текста: старая версия выбрасывала [p.s.] и всё от [img of the report] до ближайшего [/img].

Новое: строгие паттерны тегов в Whitelist

У Bitrix\UI\Format\BBCode\Whitelist три новых публичных статических метода:

  • getTagPattern(array $tags) — одиночный тег из списка в строгой форме, открывающий или закрывающий; в паттерне есть голый /, так что разделитель берите другой, например #;
  • getPairedTagPattern(array $tags) — целый блок [tag]…[/tag], содержимое захватывается лениво;
  • getForbiddenBlockTags() — блочная половина запрещённых тегов.

Паттерны возвращаются без разделителей. Модификаторы iu добавляете сами, для парного ещё и s. Маркер списка [*] оба метода пропускают, его вызывающий код обрабатывает отдельно.

        <?php declare(strict_types=1);

use Bitrix\Main\Loader;
use Bitrix\UI\Format\BBCode\Whitelist;

Loader::requireModule('ui');

$tag = '#' . Whitelist::getTagPattern(['b', 'p', 'url']) . '#iu';

foreach (['[b]', '[/b]', '[p=left]', '[url=https://bxmax.ru]', '[b 2024]', '[p.s.]'] as $token) {
    printf("%-24s %s\n", $token, preg_match($tag, $token) === 1 ? 'тег' : 'текст');
}
// [b], [/b], [p=left], [url=https://bxmax.ru] → тег
// [b 2024], [p.s.] → текст

$media = '#' . Whitelist::getPairedTagPattern(Whitelist::getMediaTags()) . '#isu';
echo preg_replace($media, ' ', 'см. [img of the report] ниже, картинка [img]https://bxmax.ru/a.png[/img]');
// см. [img of the report] ниже, картинка

    

Этот код я прогнал на Whitelist.php из этого релиза. Старый медиа-паттерн на той же строке оставлял только см..

Новое: расширение ui.action-panel

Новая панель групповых действий на TypeScript, типы лежат рядом в dist/action-panel.bundle.d.ts. Экспорты: ActionPanel, ActionPanelItem, PositionTracker, OverflowCalculator. В config.php есть настройка useAirDesign, она включается, если определена константа AIR_SITE_TEMPLATE.

Панель надо брать как BX.UI.AirActionPanel, потому что имя BX.UI.ActionPanel занято. Бандл кладёт классы в BX.UI, туда же старый ui.actionpanel кладёт свой BX.UI.ActionPanel, а старый зависит от нового, грузится позже и перезаписывает глобал (так написано в src/index.ts). На странице с легаси-расширением BX.UI.ActionPanel остаётся старым конструктором.

        <?php declare(strict_types=1);

use Bitrix\Main\UI\Extension;

Extension::load('ui.action-panel');
?>
<div id="orders-grid"></div>
<script>
	const panel = new BX.UI.AirActionPanel({
		renderTo: () => document.getElementById('orders-grid'),
		ariaLabel: 'Действия с заказами',
		items: [
			{ id: 'export', text: 'Выгрузить', onclick: () => exportSelected() },
			{ id: 'delete', text: 'Удалить', onclick: (event, item) => deleteSelected() },
		],
		onResetAll: () => resetSelection(),
	});

	panel.setTotalSelectedItems(3);
	panel.show();
</script>

    

renderTo принимает элемент или функцию. Контейнер может пересоздаться ajax-перерисовкой, и тогда панель около секунды повторяет попытку найти его на каждом кадре. text у кнопки вставляется текстовым узлом, разметка в нём покажется как есть. onclick принимает только функцию: строковые обработчики, которые старая панель превращала в атрибут onclick, остались в легаси-адаптере. Ещё у кнопки есть menuItems (меню ui.system.menu), closeOnItemClick, disabled, hidden, dataset, popupDataset и attributes, у панели — refreshOverflow(), getHiddenItems(), getItemById() и статический getInstanceByNode().

Мелочи и находки

  • Легаси-панель пользуется новым расширением только частично. Она импортирует оттуда PositionTracker и OverflowCalculator, а разметку и кнопки рисует по-старому.
  • FocusTrap из ui.a11y при isolateOutside ставил inert на соседние элементы, и каждая ловушка вела свой список уже инертных, а при выключении заново искала соседей по текущему DOM. Теперь соседи запоминаются при включении, счётчик владельцев общий на все ловушки, и inert снимается, только когда элемент отпустили все, и только если он не был инертным до них.
  • У BX.UI.ActionPanel.Item в split-кнопке класс иконки приклеивался к ui-btn-link без пробела. Починили.
  • Подменю кнопок легаси-панели получили свой z-index. ActionPanel.Item.openSubMenu() и раньше передавал попапу this.actionPanel.zIndex + 1, но панель опцию zIndex никуда не сохраняла, и подменю всегда получало null. Теперь конструктор кладёт её в this.zIndex.
  • ui.action-panel приехал только с lang/ru/config.php, других языков в списке добавленных файлов нет.
  • В ui.block-diagram автоскролл больше не стартует по нажатию, только с первым движением мыши. Раньше клик по блоку у края холста утаскивал холст. Если правило валидации порта отказало цели при протягивании связи, отказ запоминается до конца жеста и правило про эту цель больше не спрашивают. Это записано контрактом в комментарии к типу DiagramValidationPortRuleFn. Порты блоков, въехавших в видимую область во время жеста, становятся доступными целями.
  • Deprecated и изменений в БД нет.

Что делать

  • Поищите в шаблонах и компонентах ui/actionpanel/panel.js, ui/actionpanel/item.js и ui/actionpanel/css/style.css. Прямые подключения замените на Extension::load('ui.actionpanel').
  • Если расширяли BX.UI.ActionPanel.prototype, проверьте пять удалённых методов и то, что вторая панель на том же контейнере теперь убивает первую.
  • Если читаете сырые значения полей rich_text, учтите, что в новых записях скобки вне тегов хранятся как &#91; и &#93;. Для вывода текста берите Converter::toPlainText(), он понимает обе формы.
  • Если результат toPlainText() у вас где-то сохранён (своя выгрузка, кэш, поисковый индекс по старым записям rich_text), пересчитайте его после обновления.

Устаревшие и удалённые API в этой версии

Символ Статус Чем заменять
/bitrix/js/ui/actionpanel/panel.js Удалено Extension::load('ui.actionpanel')
/bitrix/js/ui/actionpanel/item.js Удалено Extension::load('ui.actionpanel')
/bitrix/js/ui/actionpanel/css/style.css Удалено Extension::load('ui.actionpanel')
BX.UI.ActionPanel.prototype.getMutationObserver() Удалено BX.UI.PositionTracker
BX.UI.ActionPanel.prototype.getMutationObserverParam() Удалено BX.UI.PositionTracker
BX.UI.ActionPanel.prototype.addHiddenItem() Удалено BX.UI.ActionPanel.prototype.fillHiddenItems()
BX.UI.ActionPanel.prototype.removeHiddenItem() Удалено BX.UI.ActionPanel.prototype.fillHiddenItems()
BX.UI.ActionPanel.prototype.handleGridUnSelectItem() Удалено —

Читайте дальше

ui 26.600.0 Ломающее Свежее

UI 26.600.0: параллельная и presigned-загрузка файлов, поле rich_text и Bootstrap JS 4.6

Дифф UI 26.600.0 даже без языковых файлов и `.min.js` весит 19 МБ, и почти весь этот объём занимают пересобранные бандлы и вендорные библиотеки (один mermaid дал 136 тысяч строк). Из содержательного загрузчик файлов научился принимать части параллельно и отдавать их браузеру для загрузки прямо в S3....

7 мин
ui 26.650.0 Рутинное Свежее

UI 26.650.0: клавиатура в пикере реакций и аудио во вьюере

Релиз на 2 МБ, из PHP в нём только `config.php` расширений и номер версии. Пикером реакций теперь можно управлять с клавиатуры (роль `menu`, стрелки, Escape), у него появились опции `priorityReaction` и `contextAction` и методы `focus()` и `destroy()`. Если пикер привязан к кнопке, ссылке или элемен...

1 мин
ui 26.687.0 Рутинное Свежее

UI 26.687.0: триал VibePlus в облаке и дизайн чипа TintedBitrixGpt

`Bitrix\UI\Controller\InfoHelper` при активации демо сначала спрашивает у модуля bitrix24, включён ли старт VibePlus, и если да, запускает триал VibePlus вместо обычного демо тарифа. Ветка срабатывает только при подключённом модуле bitrix24 и определённой константе `BX24_HOST_NAME`. В `ui.system.chi...

1 мин
voximplant 26.700.0 Ломающее Свежее

Voximplant 26.700.0: оценку качества связи убрали, у notifyAdmins() появился тип

Модуль телефонии voximplant 26.700.0 вышел в коробочный Битрикс24 11 сентября 2026 года, по данным официального канала «Битрикс24 changelog». Из карточки звонка убрали оценку качества связи вместе с её JS-событиями, у `Im::notifyAdmins()` появился тип параметра, а публичные константы `SipStatusInfor...

3 мин
Мы используем файлы cookie для улучшения работы сайта. Продолжая использовать сайт, вы соглашаетесь с нашей политикой конфиденциальности.
AI Домовой

AI Домовой История

на связи

пишет…
Нет истории чатов
AI Домовой

Нужна авторизация

Войдите, чтобы задавать вопросы AI Домовому.

Войти