Начните использовать BlockBuilder за несколько минут
npm install @mushket-co/block-builderBlockBuilder доступен в двух версиях: FREE (бесплатная демо-версия) и PRO (платная версия с полным функционалом).
BlockBuilder автоматически скрывает недоступные функции в FREE версии:
type: 'custom') не отображаютсяtype: 'api-select') не отображаютсяДля демо, проверки и ознакомления с возможностями на localhost доступен тестовый ключ PRO лицензии. Вы можете попробовать все функции, потыкать и посмотреть функционал:
BB-PRO-1234-5678-ABCDЭтот ключ работает только на localhost. Для production используйте ключ, полученный при покупке.
При переходе с FREE на PRO лицензию, UI автоматически обновляется без перезагрузки страницы. Все PRO функции становятся доступными сразу после активации лицензии.
Конфигурация типа блока
Основной класс для работы с блоками
Колбэк для сохранения блоков (localStorage, API и т.д.)
Рендереры для различных типов полей
import { BlockBuilder } from '@mushket-co/block-builder'
const blockConfigs = [
{
type: 'text',
label: 'Текстовый блок',
fields: [
{
name: 'content',
label: 'Содержимое',
type: 'text',
required: true
}
],
defaultProps: {
content: 'Привет, мир!'
},
spacingOptions: { // Автоматически добавит поле spacing в форму
enabled: true,
spacingTypes: ['padding-top', 'padding-bottom']
}
}
]
const blockBuilder = new BlockBuilder({
containerId: 'my-app',
blockConfigs: blockConfigs,
autoInit: true,
onSave: async (blocks) => {
// Сохранение блоков через колбэк (например, на сервер или в localStorage)
localStorage.setItem('blocks', JSON.stringify(blocks))
return true
}
})Каждый блок в blockConfigs должен иметь следующую структуру:
const blockConfigs = {
// Ключ объекта - это type блока (обязательно, уникальный)
text: {
// Основные свойства блока
type: 'text', // Тип блока (обязательно, должен совпадать с ключом)
label: 'Текстовый блок', // Отображаемое название в UI (обязательно)
title: 'Текстовый блок', // Альтернативное название (опционально)
icon: '📝', // Иконка блока (опционально)
description: 'Блок для текстового контента', // Описание блока (опционально)
// Конфигурация рендеринга блока
render: {
kind: 'html', // 'html' для Pure JS или 'component' для Vue/React
template: (props) => { // Функция шаблона (для kind: 'html')
return '<div>' + props.content + '</div>'
},
// ИЛИ для Vue компонента:
// kind: 'component',
// framework: 'vue',
// component: TextBlockComponent
},
// Поля формы редактирования
fields: [
{
field: 'content', // Имя поля (обязательно)
label: 'Содержимое', // Метка поля (обязательно)
type: 'textarea', // Тип поля (обязательно)
placeholder: 'Введите текст...',
defaultValue: '', // Значение по умолчанию
rules: [ // Правила валидации
{ type: 'required' },
{ type: 'minLength', value: 10 }
]
}
],
// Свойства блока по умолчанию
defaultProps: {
content: 'Привет, мир!',
textAlign: 'left'
},
// Настройки блока по умолчанию
defaultSettings: {
visible: true
},
// Опции для автоматического добавления spacing полей
spacingOptions: {
enabled: true, // Включить spacing (по умолчанию true)
spacingTypes: [ // Какие типы отступов доступны
'padding-top',
'padding-bottom',
'margin-top',
'margin-bottom'
],
config: {
min: 0, // Минимальное значение (по умолчанию 0)
max: 200, // Максимальное значение (по умолчанию 200)
step: 4, // Шаг изменения (по умолчанию 1)
breakpoints: [ // Кастомные брекпоинты (только PRO)
{ name: 'desktop', label: 'Desktop', maxWidth: undefined },
{ name: 'tablet', label: 'Tablet', maxWidth: 1024 },
{ name: 'mobile', label: 'Mobile', maxWidth: 640 }
]
}
}
}
}type (обязательный)Уникальный идентификатор типа блока. Должен совпадать с ключом объекта в blockConfigs.
label (обязательный)Отображаемое название блока в UI (например, в списке доступных типов блоков для добавления).
title (опциональный)Альтернативное название блока. Если не указано, используется label.
icon (опциональный)Иконка блока (эмодзи или строка). Отображается в UI рядом с названием блока.
description (опциональный)Описание блока. Используется для подсказок пользователю о назначении блока.
render (обязательный)Конфигурация рендеринга блока. Определяет, как будет отображаться блок.
Для Pure JS используйте kind: 'html' с функцией template:
render: {
kind: 'html',
template: (props) => '<div>' + props.content + '</div>'
}Для Vue используйте kind: 'component':
render: {
kind: 'component',
framework: 'vue',
component: TextBlockComponent // Vue компонент
}fields (опциональный)Массив полей формы для редактирования блока. Подробное описание типов полей см. в разделе "Поля форм".
defaultProps (опциональный)Объект со значениями свойств по умолчанию для нового блока. Эти значения будут использоваться при создании блока.
defaultSettings (опциональный)Объект с настройками блока по умолчанию (например, visible: true).
spacingOptions (опциональный)Опции для автоматического добавления spacing полей в форму редактирования. Если enabled: true, BlockBuilder автоматически добавит поля для управления отступами блока с поддержкой адаптивности.
blockConfigs должен совпадать со значением typetype и label являются обязательнымиrender обязательно для определения способа рендеринга блокаBlockBuilder автоматически управляет отступами блоков через систему spacing. Отступы хранятся в block.props.spacingи автоматически применяются к блоку с поддержкой адаптивности.
spacingOptions в конфигурации блока, BlockBuilder автоматически добавляет поле spacing в форму редактированияblock.props.spacing// block.props.spacing
{
desktop: {
'padding-top': 20,
'padding-bottom': 20,
'margin-top': 10,
'margin-bottom': 10
},
tablet: {
'padding-top': 16,
'padding-bottom': 16,
'margin-top': 8,
'margin-bottom': 8
},
mobile: {
'padding-top': 12,
'padding-bottom': 12,
'margin-top': 6,
'margin-bottom': 6
}
}BlockBuilder автоматически применяет spacing к блоку. В Vue компонентах это происходит через автоматическую генерацию inline стилей:
// BlockBuilder автоматически применяет spacing к элементу блока
// Результат:
<div
class="block-builder-block"
style="
margin-top: 10px;
margin-bottom: 10px;
--spacing-padding-top: 20px;
--spacing-padding-bottom: 20px;
"
>
<!-- Ваш контент блока -->
<!-- Используйте CSS переменные для padding внутри блока -->
<div style="padding-top: var(--spacing-padding-top); padding-bottom: var(--spacing-padding-bottom);">
{{ block.props.content }}
</div>
</div>Если вы создаете свой компонент блока, используйте утилиты BlockBuilder:
// Vue 3 компонент
import { getBlockInlineStyles } from '@mushket-co/block-builder/core';
export default {
props: ['block'],
computed: {
spacingStyles() {
// Получаем стили для текущего брекпоинта
return getBlockInlineStyles(
this.block.props.spacing,
'spacing',
this.customBreakpoints // опционально, свои брекпоинты
);
}
},
template: '<div :style="spacingStyles"><div style="padding-top: var(--spacing-padding-top);">{{ block.props.content }}</div></div>'
}
// Pure JS
import { applySpacingToElement } from '@mushket-co/block-builder/core';
const element = document.getElementById('my-block');
applySpacingToElement(
element,
block.props.spacing,
'spacing',
customBreakpoints // опционально
);Padding передается через CSS переменные, чтобы вы могли контролировать, куда именно применить padding внутри блока:
/* Используйте CSS переменные в ваших стилях */
.block-content {
padding-top: var(--spacing-padding-top, 0);
padding-bottom: var(--spacing-padding-bottom, 0);
}
/* Или напрямую в inline стилях */
<div style="padding-top: var(--spacing-padding-top);">
Контент блока
</div>BlockBuilder автоматически отслеживает изменение размера окна и обновляет отступы в зависимости от текущего брекпоинта. Это происходит без перезагрузки страницы и без дополнительной настройки.
Для полноценной работы BlockBuilder в продакшене вам необходимо реализовать следующие API эндпоинты на вашем бэкенде:
POST/api/blocks/saveЭндпоинт для сохранения массива блоков. Принимает JSON строку с блоками и сохраняет её в вашей базе данных.
POST /api/blocks/save
Content-Type: application/json
{
"blocks": [
{
"id": "block-1",
"type": "text",
"props": {
"content": "Текст блока",
"spacing": {
"desktop": { "padding-top": 20, "padding-bottom": 20 }
}
}
}
]
}HTTP/1.1 200 OK
Content-Type: application/json
{
"success": true,
"message": "Блоки успешно сохранены"
}Важно: Блоки передаются как массив объектов. Вы можете сохранять их как JSON строку в базе данных или как отдельные записи для каждого блока. Рекомендуется хранить весь массив блоков как единую JSON строку для конкретной страницы/контекста.
GET/api/blocks/loadЭндпоинт для получения сохраненных блоков. Возвращает массив блоков в формате JSON.
GET /api/blocks/load
Content-Type: application/jsonHTTP/1.1 200 OK
Content-Type: application/json
{
"blocks": [
{
"id": "block-1",
"type": "text",
"props": {
"content": "Текст блока",
"spacing": {
"desktop": { "padding-top": 20, "padding-bottom": 20 }
}
}
}
]
}Примечание: Если блоков нет, верните пустой массив []. Это позволит BlockBuilder корректно инициализироваться с пустым состоянием.
POST/api/uploadЭндпоинт для загрузки статических файлов (изображений). Принимает файл через FormData и возвращает URL загруженного файла.
POST /api/upload
Content-Type: multipart/form-data
FormData:
file: [изображение]
(опционально) Authorization: Bearer tokenHTTP/1.1 200 OK
Content-Type: application/json
{
"url": "https://example.com/uploads/image.jpg",
"width": 1920,
"height": 1080,
"size": 245678
}Важно: Ответ сервера ОБЯЗАТЕЛЬНО должен содержать поле url(или src) с URL загруженного файла. Если формат ответа отличается, используйтеresponseMapper в конфигурации поля image для преобразования ответа.
Рекомендация: Загружайте файлы на статический сервер (CDN, S3, или вашу файловую систему) и возвращайте полный URL. Это позволяет хранить в блоке только ссылку на файл, а не сами данные изображения.
// Ошибка валидации
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"success": false,
"error": "Неверный формат данных блоков",
"details": "Поле 'type' обязательно для каждого блока"
}
// Ошибка загрузки файла
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"success": false,
"error": "Файл слишком большой",
"maxSize": "5242880"
}
// Серверная ошибка
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
{
"success": false,
"error": "Внутренняя ошибка сервера"
}