Skip to content

Отображение документов ​

EditorRenderer превращает сохранённый документ Rector в DOM без элементов редактирования, истории, выделения и панелей инструментов. Он предназначен для страниц статей, предварительного просмотра, экспорта и любого представления, которому нужен только итоговый документ.

Синхронная точка входа рендерера содержит полный встроенный набор. Её граф модулей включает интеграции Gallery и Person, поэтому до импорта @shelamkoff/rector/renderer установите их дополнительные пакеты из peerDependencies:

bash
npm install @shelamkoff/rector @shelamkoff/carousel @shelamkoff/expose @shelamkoff/masonry

Приложению, которое не импортирует рендерер, эти пакеты не нужны. blockTypes ограничивает создаваемые рендереры, но не может изменить разрешение модулей ESM после импорта точки входа.

Основное использование ​

js
import { createEditorRenderer } from '@shelamkoff/rector/renderer'

const renderer = createEditorRenderer({ theme: 'dark' })
renderer.renderTo(documentData, document.querySelector('#article'))

// При удалении представления:
renderer.destroy()

Рендерер напрямую принимает документ из editor.save(). Метаданные time, version и id блока для отображения необязательны. Функциональным входом служат blocks, type каждого блока и данные плагина.

Конфигурация ​

ts
interface RendererConfig {
  injectStyles?: boolean
  classPrefix?: string
  throwOnUnknown?: boolean
  theme?: 'dark' | 'light'
  validationMode?: 'preserve' | 'strict'
  onValidationError?: (issue: { blockId?: string; type: string }) => void
  locale?: Record<string, LocaleValue>
  blockTypes?: BlockType[]
  blockConfigs?: {
    poll?: PollRendererConfig
    [type: string]: unknown
  }
  inlinePlugins?: InlinePluginLike[]
}
ПараметрЗначение по умолчаниюНазначение
injectStylestrueавтоматически загрузить базовые стили и стили зарегистрированных рендереров при первом отображении
classPrefixeditorпространство имён классов результата
throwOnUnknowntrueвыбросить исключение для незарегистрированного блока вместо вывода заглушки
themedarkтема рендерера
validationModepreserveнормализовать ошибочные данные встроенного блока или выбросить исключение до отображения в режиме strict
onValidationErrorнетполучить не содержащие пользовательских данных { blockId?, type } для ошибочного встроенного блока
localeвстроенный английскийплоский словарь сообщений renderer.*
blockTypesвсе встроенныесоздать только выбранные встроенные рендереры
blockConfigsнетоперативная конфигурация по типу встроенного блока
inlinePluginsнетфабрики для восстановления постоянных внутристрочных виджетов

По умолчанию отсутствие рендерера приводит к явной ошибке. Устанавливайте throwOnUnknown: false только тогда, когда приложение со смешанными версиями намеренно допускает заглушку вместо неподдерживаемого блока. Заглушка получает класс <classPrefix>-unknown и атрибут data-block-type, но не воспроизводит отсутствующее содержимое.

Проверка сверяет структуры данных и правила URL встроенных блоков до вызова их рендереров. В режиме preserve ошибочные данные заменяются нормализованной безопасной формой встроенного типа, а затем вызывается onValidationError; в режиме strict вместо этого выбрасывается InvalidBlockDataError. Пользовательский рендерер с тем же типом владеет собственным контрактом проверки, поэтому замена встроенного рендерера одновременно отключает его встроенную проверку.

blockConfigs.poll принимает те же оперативные параметры dataSource, compareRevisions, onError и maxVoters, что и плагин опроса в редакторе. compareRevisions(next, current) должна возвращать положительное число, только если next новее; без неё разные непрозрачные редакции применяются в порядке получения. Рендерер загружает актуальные результаты и подписывается на них, не изменяя переданный документ. Вызов destroy() отменяет запросы и подписку.

Контракты типизации ​

Исполняемые значения рендерера импортируются из @shelamkoff/rector/renderer, а его декларации — из отдельной точки входа только для типов:

ts
import { createEditorRenderer } from '@shelamkoff/rector/renderer'
import type {
  BlockRenderer,
  OutputData,
  ParagraphBlock,
  RendererConfig,
} from '@shelamkoff/rector/renderer/types'

Точка входа типов экспортирует:

  • оболочку документа OutputData, общую форму блока OutputBlockData и InlineWidget;
  • Block, BlockType и отдельный псевдоним наподобие ParagraphBlock, ImageBlock или PollBlock для каждого встроенного рендерера;
  • соответствующие контракты данных, включая ParagraphData, ImageData, GalleryData, CarouselData, PollData и PersonData;
  • контракты расширения BlockRenderer, InlineParser, InlinePluginLike и RendererConfig;
  • контракты интеграции опроса PollDataSource, PollResults, PollVoter и PollRendererConfig.

Используйте OutputData, если приложение только отображает документы и не зависит от типов редактора. EditorDocument из editor.save() структурно совместим с ним и не требует преобразования. Для рендерера известного встроенного типа используйте соответствующий псевдоним блока, а для собственного типа приложения — OutputBlockData<'callout', CalloutData>.

Методы отображения ​

renderBlock(block) возвращает элемент одного блока или применяет выбранную политику неизвестного типа. Полученный элемент принадлежит этому экземпляру рендерера; перед удалением освободите его через renderer.destroy(element).

render(data) возвращает оболочку со всеми блоками. Место добавления выбирает вызывающая сторона, но освобождением продолжает владеть рендерер; для него вызовите renderer.destroy(wrapper).

renderTo(data, container) заменяет принадлежащий рендереру результат в контейнере и запоминает элементы для последующего освобождения.

js
const wrapper = renderer.render(documentData)
preview.replaceChildren(wrapper)

Для многократно обновляемого контейнера предпочтительнее renderTo(), потому что владение освобождением выражено явно.

Частичные обновления и редакции блоков ​

renderTo() сопоставляет блоки по устойчивому id и текущему порядку. Неизменившийся блок сохраняет тот же элемент DOM и принадлежащие рендереру ресурсы; заменяется только изменившийся блок. Повторная регистрация или удаление рендерера отменяет повторное использование элементов этого типа.

Если у блока нет revision, Rector сравнивает полную сигнатуру его type, data, tunes и inline. Источник данных, уже поддерживающий достоверную редакцию содержимого или устойчивый хеш, может передать его в block.revision. Тогда Rector сравнит это значение за постоянное время без сериализации данных блока:

js
const block = {
  id: 'intro',
  revision: 'sha256:9f4c…',
  type: 'paragraph',
  data: { text: 'Здравствуйте' },
}

Меняйте revision при каждом изменении data, tunes или inline. Одинаковая редакция для разного содержимого намеренно сообщает рендереру, что блок не изменился. Редактор сохраняет входящую редакцию, пока блок не тронут, и удаляет её после первого локального изменения, потому что не может создать следующую редакцию от имени источника данных.

Регистрация рендереров ​

Встроенные рендереры регистрируются по умолчанию. Дополнительный или заменяющий рендерер должен иметь тот же type, что и сохранённый блок:

js
renderer.registerRenderer(createCalloutRenderer('article'))

renderer.hasRenderer('callout')
renderer.getRegisteredTypes()
renderer.unregisterRenderer('callout')

Повторная регистрация типа заменяет рендерер для будущего результата. До удаления рендерера с ресурсами в destroy(element) освободите уже добавленный им результат.

Загрузка только используемых типов ​

Для уменьшения исходного графа создавайте плагины и рендереры по документу или явному списку типов через асинхронные точки входа.

js
import { createBlockPluginsAsync } from '@shelamkoff/rector/plugins/async'
import { createDefaultRenderersAsync } from '@shelamkoff/rector/renderer/async'

const plugins = await createBlockPluginsAsync(documentData)
const renderers = await createDefaultRenderersAsync('oe', {}, documentData)

Асинхронные вспомогательные функции работают только с известными публичными типами. Неизвестный тип отклоняется, а не превращается в произвольный путь импорта.

Внутристрочное форматирование и виджеты ​

Каждый рендерер блока получает parseInline(text). Функция очищает поддерживаемую внутристрочную разметку и возвращает DocumentFragment. Добавляйте этот фрагмент в DOM вместо назначения innerHTML.

Постоянным виджетам также нужен облегчённый плагин стороны рендерера с type, createWidget(data, id) и getData(element). createWidget() обязан сохранить переданный идентификатор в data-id.

mapTextFields() рендерера блока должен перечислять ровно те же поля с HTML, что и редакторский плагин. Rector раскрывает заполнители {{id}} до вызова рендерера.

Стили ​

При стандартном injectStyles: true первый рендеринг автоматически получает уникальные URL базовых таблиц и стилей зарегистрированных рендереров. Уничтожение последнего результата или полный renderer.destroy() освобождает этого владельца. getStyleUrls() возвращает URL, а injectStyles() остаётся доступным для явной предварительной загрузки и возвращает независимый дескриптор освобождения.

js
import '@shelamkoff/rector/styles/renderer.css'
import '@shelamkoff/rector/renderer/renderers/gallery/styles.css'

const renderer = createEditorRenderer({
  blockTypes: ['gallery'],
  injectStyles: false,
})
renderer.renderTo(documentData, container)

Не импортируйте CSS интерфейса редактора ради итогового документа. В режиме сборщика используйте @shelamkoff/rector/styles/renderer.css вместе с subpath renderer/renderers/<type>/styles.css каждого выбранного рендерера. Если размер пакета не важен, доступна единая точка @shelamkoff/rector/styles.css.

Освобождение ресурсов ​

destroy(target) принимает контейнер, ранее переданный в renderTo(), оболочку из render() или элемент из renderBlock(). Метод вызывает необязательный destroy(element) владеющего рендерера блока, очищает принадлежащий рендереру результат и забывает его. destroy() без аргумента освобождает все результаты этого экземпляра.

Контейнер приложения не удаляется. Освобождаются наблюдатели, обработчики и сторонние экземпляры, после чего запись об установке забывается. Автоматически подключённые стили освобождаются, когда не остаётся принадлежащих рендереру результатов. Отдельный дескриптор, возвращённый явным injectStyles(), имеет собственный жизненный цикл и должен быть освобождён своим методом destroy().

Граница безопасности ​

Данные рендерера могут поступить из хранилища или внешней службы и остаются недоверенными. Встроенные рендереры используют общую очистку HTML, URL и стилей. Пользовательский рендерер обязан применять textContent, parseInline(), безопасные методы атрибутов и явные разрешающие списки. Нельзя считать содержимое безопасным только потому, что оно ранее прошло через редактор.

Точные данные блоков и специальные зависимости перечислены в каталоге рендереров.

Rector is released under the MIT License.