Skip to content

Custom Containers

Custom Containers позволяют расширять функционал редактора своими клиентскими контейнерами без разработки отдельного виджета внутри Verstka.

Клиент описывает контейнеры в конфиге: как они называются, какого они размера, какую иконку показывать в панели создания, какой HTML рендерить, какие параметры можно менять в редакторе и какой маркер оставить в итоговом HTML для замены на стороне сайта.

Для чего это нужно

Самый частый сценарий - вставить в статью место под клиентский виджет, а затем заменить это место на сайте клиента обычным строковым replace по HTML-комментарию:

html
<!-- ReplaceMarker: banner -->

Например, редактор расставляет контейнеры banner, article-authors, buy-button, а сайт клиента после получения HTML заменяет маркеры на реальные CMS-виджеты, рекламные блоки, авторские карточки или кнопки покупки.

Другой сценарий - собрать конфиг на стороне клиента уже с реальными данными статьи. Например, если клиентская админка знает авторов статьи, текущую рубрику или товар, она может сразу положить эти данные в html или customProps.

Передача конфига

Конфиг передается в metadata.customContainers при открытии сессии:

  • интеграция: POST /integration/session/open;
  • staff-страница /new: backend читает app/staff/custom_containers.json и кладет его в metadata.

Редактор получает конфиг из GET /session/{id} как поле customContainers.

Формат:

ts
type ClientConfig = {
  customContainers: CustomContainerDefinition[]
}

Контейнеров может быть любое количество. Каждый контейнер должен иметь уникальный id. Значение — массив определений, не объект и не map.

Конфиг сохраняется в metadata_json при создании сессии. Повторный session/open для уже существующего материала metadata (и customContainers) не обновляет.

Как это работает

  1. Клиент формирует customContainers на своей стороне.
  2. Verstka сохраняет массив в metadata сессии и отдает его в GET /session.
  3. В редакторе появляется отдельная кнопка создания для каждого контейнера.
  4. Дизайнер размещает контейнер в статье и, если нужно, меняет параметры через правую панель.
  5. В итоговом HTML рендерится контейнер, его атрибуты, HTML из конфига и, если указан replaceMarker, комментарий вида <!-- ReplaceMarker: value -->.
  6. Сайт клиента заменяет маркер или использует HTML/атрибуты контейнера для собственной логики.

Access in code

У любого контейнера в редакторе есть отдельная секция Access in code. По умолчанию codeName равен null, поэтому в HTML не появляется лишний атрибут.

Если дизайнер нажимает плюс в этой секции и задает Code name, непустое значение попадает в итоговую разметку как публичный атрибут:

html
<div data-vrstk-container-code-name="heroImage">...</div>

Это удобно для embed-кода и клиентской интеграции, когда нужно найти конкретный контейнер через DOM. Для кода без iframe можно искать в текущем document; для iframe доступ к window.parent.document возможен только при same-origin условиях браузера.

Формат контейнера

ts
type CustomContainerDefinition = {
  id: string
  name: string
  toolIcon: string
  width: number
  height: number
  resizable?: boolean
  dummyImageUrl?: string | null
  html?: string
  replaceMarker?: string | null
  attributes?: Record<string, string | number | boolean | null>
  customProps?: Record<string, string | number | boolean | null>
  tool?: {
    sectionsConfig: Record<string, { widgetProperties: string[] }>
    controlsConfig: Record<string, CustomControlConfig>
  }
}

После нормализации в редакторе опциональные поля получают значения по умолчанию: resizable: true, html: '', dummyImageUrl: null, replaceMarker: null, пустые attributes / customProps / tool.

Поля

ПолеТипОписание
idstringУникальный технический ID контейнера. Используется в сохраненных данных и маркерах.
namestringНазвание в UI редактора и текст editor-only заглушки внутри пустого контейнера.
toolIconstringИконка кнопки создания: inline SVG строкой или имя иконки редактора (например version).
widthnumberСтартовая ширина контейнера в редакторе.
heightnumberСтартовая высота контейнера в редакторе.
resizablebooleanМожно ли менять размер контейнера в редакторе. Это общее свойство всех контейнеров. По умолчанию true.
dummyImageUrlstring | nullURL картинки-заглушки для canvas-редактора. В превью и на сайте клиента не рендерится.
htmlstringHTML, который будет отрендерен внутри контейнера. Может включать <style>.
replaceMarkerstring | nullЕсли задан, в HTML появится <!-- ReplaceMarker: value -->.
attributesRecordСтатические параметры контейнера. В HTML попадают как data-vrstk-custom-param-*.
customPropsRecordНачальные значения параметров, которые могут редактироваться через контролы. В HTML — те же data-vrstk-custom-param-*.
toolobjectОписание контролов правой панели. Если пустой, у контейнера нет настроек.

Правила рендера

Custom Container ведет себя по тому, какие поля есть в конфиге:

Что есть в конфигеЧто рендерится
html непустойСанитизированный HTML внутри контейнера.
html пустой (editor)dummyImageUrl, если задан; иначе placeholder с текстом name.
html пустой (preview/SSR)Пустой контейнер без визуальной заглушки.
replaceMarker заданHTML-комментарий <!-- ReplaceMarker: value -->.
tool.controlsConfig заданКонтролы в правой панели редактора (только whitelist типов).
resizable: falseВ редакторе скрываются resize-ручки контейнера.

HTML очищается перед выводом: удаляются <script>, inline-обработчики событий и небезопасные URL вроде javascript:.

dummyImageUrl и fallback с name нужны только в canvas-редакторе, чтобы дизайнер видел пустой контейнер и мог его выбрать. В превью, SSR и на сайте клиента эти заглушки не попадают в разметку: остается сам контейнер с data-vrstk-custom-id, параметрами и комментарием-маркером.

И attributes, и customProps сериализуются одинаково: data-vrstk-custom-param-{kebab-case}. При совпадении ключей значение из customProps побеждает. null пропускается.

Маркер замены

replaceMarker нужен для простого и надежного server-side replacement на сайте клиента.

Конфиг:

ts
{
  id: 'banner',
  name: 'Баннер',
  replaceMarker: 'banner',
  html: '',
  dummyImageUrl: null,
  width: 970,
  height: 250,
  resizable: false,
  toolIcon: '<svg viewBox="0 0 16 16">...</svg>',
  attributes: {},
  customProps: {},
  tool: {
    sectionsConfig: {},
    controlsConfig: {}
  }
}

В HTML появится:

html
<!-- ReplaceMarker: banner -->

На PHP это можно заменить обычным str_replace:

php
$articleHtml = str_replace(
    '<!-- ReplaceMarker: banner -->',
    renderBannerWidget($article),
    $articleHtml
);

HTML с реальными данными

Так как конфиг собирается на клиентской стороне, в html можно положить уже готовую разметку с реальными данными статьи:

  • авторы статьи;
  • текущая рубрика;
  • ID товара;
  • название бренда;
  • текст дисклеймера;
  • HTML, который клиент уже умеет собирать в своей CMS.

Важно: значения из CMS нужно экранировать до вставки в HTML. Verstka дополнительно очищает HTML, но это не заменяет нормальную обработку данных на стороне клиента.

Контролы

Контролы позволяют дать дизайнеру изменить параметры контейнера в редакторе. Эти значения сохраняются в customProps и попадают в итоговый HTML как параметры контейнера.

Подробное описание доступных контролов: Контролы.

Готовые сценарии: Примеры.