Перейти к содержимому
ФреймворкJavaScript

Класс Blok: создание и уничтожение редактора

Главный класс редактора. Он создаёт экземпляр редактора Blok и управляет им. Любое пространство имён, доступное инструменту через api.*, есть и на самом экземпляре как editor.*. Свойства ниже описывают ту же поверхность плюс пространства имён width, placeholder, tokens и i18n, которые класс объявляет сам.

Обновлено 7 окт. 2026 г.Редактировать на GitHub

Как получить экземпляр редактора

Методы ниже вызываются на редакторе, созданном через new Blok(). Они доступны после того, как разрешится editor.isReady.

TypeScript
// You already hold the instance returned by the constructor.
const editor = new Blok({ holder: 'editor' });
await editor.isReady;

// Call any API method on it.
editor.caret.setToLastBlock('end');

Методы

save()

Promise<OutputData>

Извлекает текущее содержимое редактора в виде структурированных JSON-данных. Это основной метод для сохранения содержимого редактора.

Поля с форматированным текстом, например paragraph.text, хранят фрагменты: массивы { text, marks? }. Документ, сохранённый старой версией, может хранить HTML-строки, пока его не сохранят снова. migrateToRichText переводит его.

Когда использовать

Вызывайте его после await editor.isReady. Возвращённый JSON и есть ваш источник истины. Сохраните его и передайте обратно в render().

Ошибки

  • Редактор находится в режиме только для чтения в момент вызова save().

    Blok's content can not be saved in read-only mode

    Вызовите readOnly.set(false) перед сохранением. Ещё можно сохранить данные из последнего вызова onSave или из собственного зеркального состояния.

TypeScript
// Save editor content
const data = await editor.save();
console.log(data.blocks); // Array of block data

render(data)

Promise<void>

Отображает содержимое редактора из ранее сохранённых JSON-данных. Принимает нестрогий формат (LooseOutputData). Значение null для data, id или time блока из backend-DTO нормализуется на границе.

Когда использовать

Загружает сохранённый контент и заменяет текущий документ. Чтобы добавить контент, а не заменить, используйте blocks.insertMany().

TypeScript
// Load saved content
const savedData = {
  blocks: [
    { id: '1', type: 'paragraph', data: { text: 'Hello' } }
  ]
};
await editor.render(savedData);

focus(atEnd?)

boolean

Устанавливает фокус на редактор. Может также поставить курсор в конец содержимого.

Когда использовать

Передайте true, чтобы поставить курсор в самый конец. Для конкретного блока или смещения используйте API caret.

TypeScript
// Focus at start
editor.focus();

// Focus at end
editor.focus(true);

clear()

Promise<void>

Удаляет всё содержимое редактора. Остаётся один пустой блок инструмента по умолчанию, поэтому редактор никогда не бывает без блоков. При этом следующий вызов save() всё равно вернёт blocks: []. Пустой блок по умолчанию не проходит валидацию и отбрасывается из результата.

Когда использовать

Удаляет все блоки и оставляет один пустой параграф. Это действие можно отменить, в отличие от destroy(), который уничтожает экземпляр.

TypeScript
// Clear all content
await editor.clear();

destroy()

void

Уничтожает экземпляр редактора и удаляет все DOM-элементы и обработчики событий.

Когда использовать

Вызывайте его из хука размонтирования вашего фреймворка, чтобы не утекли слушатели. После этого экземпляр использовать нельзя. Создайте новый Blok, чтобы начать заново.

TypeScript
// Clean up on component unmount
editor.destroy();

handlers.set(handlers)

void

Устанавливает, заменяет или удаляет живые колбэки редактора на месте: onChange, onSave, onEnter, onSubmit, onBeforeRender, onAfterRender. Замена идёт на месте, поэтому курсор, выделение, прокрутка и история отмен сохраняются.

Затрагиваются только переданные ключи. Ключ со значением undefined СНИМАЕТ обработчик. Это важно, потому что само наличие колбэка задаёт поведение.

  • С onSubmit клавиша Enter выполняет сериализацию и отправку вместо разбиения блока.
  • С onSave включается конвейер отслеживания изменений.

Используйте метод, чтобы сделать колбэк реактивным без пересоздания редактора. Адаптеры React, Vue и Angular вызывают этот сеттер за вас, когда prop, слушатель или колбэк из [config] появляется или исчезает.

Когда использовать

Затрагиваются только переданные ключи. Ключ со значением undefined снимает обработчик. Именно так режим «Enter отправляет» выключается обратно.

Параметры

ПараметрТипОбязательныйПо умолчаниюОписание
handlersLiveHandlersОбязательный—

Частичная карта живых колбэков. Пропущенные ключи остаются как есть. Ключ со значением undefined снимает обработчик.

TypeScript
// "Enter sends" while composing, default Enter while editing a draft
editor.handlers.set({
  onSubmit: sendsOnEnter ? (data) => send(data) : undefined,
});

// Start mirroring content into your store, later stop again
editor.handlers.set({ onSave: (data) => store.set(data) });
editor.handlers.set({ onSave: undefined });

whenAllReady(options?)

Promise<void>

Статический метод. Он резолвится, когда каждый экземпляр Blok в заданной области завершил загрузку, то есть его isReady завершился. Отклонение тоже считается завершением. Это общий сигнал готовности для страниц с несколькими экземплярами, он заменяет ручную сборку колбэков onReady по каждому экземпляру.

  • Передайте within (Element), чтобы учитывать только экземпляры внутри вашего поддерева.
    • Тогда посторонний редактор на странице не сможет держать проверку закрытой.
  • Передайте settleOn: 'rendered', чтобы готовность означала не конец конструктора, а появление контента в DOM. Это покрывает и повторные рендеры через render(data) после загрузки.

Пустая область резолвится сразу. Экземпляры, появившиеся пока промис в ожидании, продлевают ожидание. Экземпляры, созданные после его резолва, не учитываются: вызовите метод снова или используйте subscribeReady() для живого сигнала.

Когда использовать

Вызывайте как Blok.whenAllReady() на классе, а не на экземпляре. Экземпляр, который ещё загружается, учитывается в любой области, даже если его обёртка ещё не добавлена в документ. Лишнее ожидание безопасно. Недостаточное является ошибкой.

TypeScript
// A comments list: N read-only bodies + a composer.
// Wait only for the editors inside this list.
await Blok.whenAllReady({
  within: listElement,
  settleOn: 'rendered',
});
composer.focus();

readyState(options?)

{ total: number; pending: number; ready: boolean }

Статический метод. Он даёт синхронный снимок готовности для области.

Снимок показывает, сколько экземпляров попадает в within. Он также показывает, сколько из них ещё не готовы на запрошенном уровне settleOn и готова ли область целиком. Пустая область возвращает ready: true, поэтому отдельная проверка «ждать нечего» не нужна.

Когда использовать

Метод достаточно дёшев, чтобы вызывать его на каждое уведомление из subscribeReady(). Он обходит зарегистрированные экземпляры и проверяет вложенность в DOM.

TypeScript
const { pending, ready } = Blok.readyState({ within: listElement });

if (!ready) {
  showSkeleton(pending);
}

subscribeReady(listener)

() => void

Статический метод. Он подписывается на изменения готовности всех экземпляров (создание, загрузка, смена состояния рендера, уничтожение) и возвращает функцию отписки. Слушатель вызывается без аргументов, поэтому перечитывайте Blok.readyState(scope), когда он сработает. Подходит для useSyncExternalStore и других адаптеров хранилищ и даёт живой сигнал вместо одноразовой защёлки.

Когда использовать

Во фреймворках лучше использовать обёртки адаптеров: useBlokReady() в @bloklabs/react и @bloklabs/vue, injectBlokReady() в @bloklabs/angular. Они оборачивают эту подписку и поиск области.

TypeScript
const unsubscribe = Blok.subscribeReady(() => {
  setReady(Blok.readyState({ within: listElement }).ready);
});

// later
unsubscribe();

createSelector(attr, value?)

string

Именованный экспорт корня пакета, а не член класса Blok. Строит CSS-селектор по значению DATA_ATTR.

  • Без value получается селектор по наличию атрибута.
  • Со значением получается селектор по равенству.

Используйте его вместе с Blok.DATA_ATTR вместо поиска по внутренним именам классов Blok, которые не входят в публичную поверхность.

TypeScript
import { DATA_ATTR, createSelector } from '@bloklabs/core';

createSelector(DATA_ATTR.element); // '[data-blok-element]'
document.querySelectorAll(createSelector(DATA_ATTR.selected, true));

icons

string

Именованные экспорты подпути @bloklabs/core/icons, а не члены класса Blok. Это собственные глифы Blok в виде SVG-строк, по одной константе Icon* на глиф (IconBold, IconPlus, IconTrash, IconWarning, …).

Это обычные строки, поэтому они подставляются прямо туда, где инструмент обязан отдать разметку: в иконку static get toolbox() инструмента и в элементы, которые возвращает renderSettings(). Подпуть поставляется со сгенерированным самодостаточным файлом деклараций (types/icons.d.ts), где перечислены все константы. Полный набор смотрите в автодополнении редактора кода.

TypeScript
import { IconBold, IconPlus } from '@bloklabs/core/icons';

class Callout {
  static get toolbox() {
    return { title: 'Callout', icon: IconPlus };
  }

  renderSettings() {
    return [{ icon: IconBold, title: 'Bold text', onActivate: () => this.toggleBold() }];
  }
}

Свойства

СвойствоТипОписание
DATA_ATTRRecord<DataAttrKey, DataAttrValue>

Именованный экспорт корня пакета (import { DATA_ATTR } from '@bloklabs/core'), а не свойство экземпляра редактора. Это стабильные имена атрибутов data-blok-*, которые Blok пишет в своём DOM.

Так и следует запрашивать DOM редактора и писать тесты на стороне хоста вместо поиска по внутренним именам классов. Рядом с ним экспортируются типы DataAttrKey / DataAttrValue и хелпер createSelector().

BLOK_FONT_SIZE_TOKENSBlokFontSizeTokens

Именованный экспорт корня пакета (import { BLOK_FONT_SIZE_TOKENS } from '@bloklabs/core'), а не свойство экземпляра редактора. Он даёт CSS-переменную, которую пишет каждый сценарий style.fontSize, в карте ровно той же формы, что и сама конфигурация (BLOK_FONT_SIZE_TOKENS.paragraph, .heading[1], .list.checklist, .bookmark.link, …).

Используйте её везде, где типографика задаётся не конфигурацией конструктора, а другим каналом: CSS-правилом для отдельной области или editor.tokens.set({ [BLOK_FONT_SIZE_TOKENS.paragraph]: '18px' }) в рантайме. Тогда имена CSS-переменных не придётся копировать руками, а переименование станет ошибкой компиляции, а не молчаливым отсутствием эффекта.

versionstring

Именованный экспорт корня пакета (import { version } from '@bloklabs/core'), а не свойство экземпляра редактора. Это версия работающего редактора, то же значение, которое попадает в OutputData.version.

PendingBlok{ isReady; isRendered; destroy(); theme; width; placeholder; tokens; i18n }

Тип, экспортируемый из корня пакета (import type { PendingBlok } from '@bloklabs/core'), а не свойство экземпляра редактора.

Это поверхность, которая гарантированно существует синхронно между new Blok(config) и разрешением isReady. Модульные API Blok (blocks, caret, history, readOnly, …) создаются асинхронно, поэтому обращение к ним раньше вернёт undefined.

PendingBlok объявляет только восемь перечисленных здесь членов. Это превращает такое окно в ошибку компиляции вместо undefined в рантайме: const pending: PendingBlok = new Blok(config); const editor = await pending.isReady;.

isReadyPromise<Blok>

Promise, который разрешается готовым экземпляром редактора. Пространства имён API ниже (blocks, caret, history, readOnly, …) создаются асинхронно. До его разрешения они равны undefined, поэтому типизируйте ссылку, которой вы владеете в этот момент, как PendingBlok.

isRenderedboolean

Синхронный флаг готовности рендера. Он равен true, когда текущая партия рендера попала в DOM, и повторяет атрибут data-blok-rendered на обёртке. Он равен false до первого рендера и во время повторного рендера. Флаг дополняет асинхронные isReady и onReady: не нужны ни await, ни колбэк, поэтому состояние монтирования можно опрашивать синхронно.

blocksBlocks

Модуль API для работы с блоками

caretCaret

Модуль API для управления курсором

historyHistory

Модуль API истории

saverSaver

Модуль API сохранения

toolbarToolbar

Модуль API панели инструментов

inlineToolbarInlineToolbar

Модуль API строчной панели

toolsTools

Модуль API инструментов

uploaderUploader

Модуль API загрузчика: загрузка ассетов с маршрутизацией по виду ассета

eventsEvents

Модуль API событий

listenersListeners

Модуль API слушателей

notifierNotifier

Модуль API уведомлений

sanitizerSanitizer

Модуль API очистки

selectionSelection

Модуль API выделения

marksMarks

Модуль API меток: операции со строчными метками с учётом диапазона

stylesStyles

Модуль API стилей

tooltipTooltip

Модуль API подсказок

readOnlyReadOnly

Модуль API режима только для чтения

uiUi

Модуль API интерфейса

themeTheme

Модуль API темы

widthWidth

Модуль API ширины

placeholderPlaceholder

Модуль API плейсхолдера

tokensTokens

Модуль API токенов темы в рантайме

i18nEditorI18n

Модуль API i18n. Всё, что инструмент получает через api.i18n, плюс метод update()

configReadonly<Pick<BlokConfig, 'linkPaste' | 'link'>>

Доступное только для чтения представление отдельных настроек редактора: опции link и linkPaste, с которыми создан этот экземпляр. Собственный строчный инструмент или инструмент ссылок читает политику ссылок хоста отсюда (как api.config), а не выводит её заново.

rectangleSelection{ cancelActiveSelection(): void; isRectActivated(): boolean; clearSelection(): void; startSelection(pageX: number, pageY: number, shiftKey?: boolean): void; endSelection(): void }

Управление выделением перетаскиванием (рамкой). Доступно также внутри инструмента как api.rectangleSelection.

  • startSelection(pageX, pageY, shiftKey?) начинает рамку от координат страницы.
  • endSelection() сбрасывает состояние перетаскивания и прячет оверлей.
  • isRectActivated() сообщает, активна ли рамка сейчас.
  • clearSelection() снимает флаг активности.
  • cancelActiveSelection() прерывает начатое выделение (clear + end).

Его вызывает другая система выделения, например выделение ячеек таблицы, когда она забирает приоритет.