Skip to content
ФреймворкJavaScript

Как создать собственный блочный инструмент

Создайте блок-инструмент с нуля — блок-выноску, которая отрисовывается, редактируется и сохраняется как любой встроенный блок.

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

Каждый встроенный блок — это просто инструмент, реализующий интерфейс BlockTool, и вы можете добавить свой. Здесь вы создадите блок Callout: редактируемую выноску, которая сохраняет свой текст и загружает его обратно, как любой другой блок.

1

Напишите класс инструмента

Блок-инструменту нужны три вещи: статический геттер toolbox, чтобы он появился в меню /, метод render(), возвращающий элемент блока, и метод save(), возвращающий данные для сохранения.

TypeScript
// callout-tool.ts
export class CalloutTool {
  private data: { text: string };

  // Shows the tool in the "/" menu.
  static get toolbox() {
    return { title: 'Callout', icon: '💡' };
  }

  constructor({ data }: { data: { text?: string } }) {
    this.data = { text: data.text ?? '' };
  }

  // Return the element Blok mounts for this block.
  render() {
    const box = document.createElement('div');
    box.classList.add('callout');
    box.contentEditable = 'true';
    box.textContent = this.data.text;
    return box;
  }

  // Return the data Blok stores when the editor is saved.
  save(block: HTMLElement) {
    return { text: block.textContent ?? '' };
  }
}
2

Зарегистрируйте его в редакторе

Передайте класс в конфигурацию tools. Ключ, который вы зададите, станет значением type блока в сохранённых данных.

TypeScript
import { Blok } from '@bloklabs/core';
import { Paragraph } from '@bloklabs/core/tools';
import { CalloutTool } from './callout-tool';

const editor = new Blok({
  holder: 'editor',
  tools: {
    paragraph: Paragraph, // the default block every empty editor starts with
    callout: CalloutTool, // the key becomes the block's `type`
  },
});
3

Используйте и сохраните

Откройте меню /, выберите Callout, введите текст в выноску и вызовите save(). Ваш блок появится в результате рядом со встроенными.

TypeScript
const data = await editor.save();

// Your block round-trips exactly like a built-in one:
// {
//   id: 'x9k2f1',
//   type: 'callout',
//   data: { text: 'Heads up — this is a callout.' },
// }

Что дальше

Добавьте метод validate(), чтобы отбрасывать пустые блоки, меню настроек через renderSettings() или обработку вставки через onPaste. Интерфейс BlockTool покрывает весь жизненный цикл — rendered(), updated(), moved() и removed().

TypeScript
// callout-tool.ts (extended)
export class CalloutTool {
  // ...constructor, render() unchanged

  save(block: HTMLElement) {
    return { text: block.textContent ?? '' };
  }

  // Drop empty callouts when the editor saves.
  validate(savedData: { text: string }) {
    return savedData.text.trim().length > 0;
  }
}

// text-color-tune.ts — a block tune adds a control to the settings menu.
export class TextColorTune {
  static isTune = true;

  render() {
    return { title: 'Text color', icon: '🎨', onActivate: () => {/* recolor */} };
  }
}
TypeScript
import { Blok } from '@bloklabs/core';
import { CalloutTool } from './callout-tool';
import { TextColorTune } from './text-color-tune';

const editor = new Blok({
  holder: 'editor',
  tools: {
    callout: { class: CalloutTool, tunes: ['textColor'] },
    // Register the tune as a tool so a block can list it by name.
    textColor: TextColorTune,
  },
});

validate() выполняется перед тем, как разрешится save(), поэтому пустая выноска никогда не попадёт в результат. Tunes подключаются по желанию для каждого инструмента — зарегистрируйте класс тюна как инструмент (класс со static isTune = true), затем укажите его имя в tunes: [...], и Blok добавит его в меню настроек блока.

Полный контракт инструмента смотрите в Tools API и загляните в BlockData за точной формой данных, которые возвращает ваш `save()`.