Ниже — рабочая схема создания компонента на основе FeedbackForm. Вы получите:
- TSX-компонент
- Клиентский скрипт (
.inline.ts) - SCSS-стили
- Подключение в layout
- Использование в контенте через HTML
- Настройку через JSON
1. Создайте компонент TSX
Файл: quartz/components/FeedbackForm.tsx
import { QuartzComponent, QuartzComponentConstructor } from "./types"
import style from "./styles/feedbackForm.scss"
// @ts-ignore
import script from "./scripts/feedbackForm.inline"
const FeedbackForm: QuartzComponent = () => {
return <div class="feedback-form-resources" aria-hidden="true"></div>
}
FeedbackForm.css = style
FeedbackForm.afterDOMLoaded = script
export default (() => FeedbackForm) satisfies QuartzComponentConstructorИдея простая: компонент подключает стили и клиентский скрипт, а сам DOM собирается уже в браузере.
2. Создайте клиентский скрипт
Файл: quartz/components/scripts/feedbackForm.inline.ts
Внутри:
- ищем элементы формы на странице
- читаем JSON-конфиг
- строим поля
- отправляем JSON на
action
Важно: Quartz использует SPA-навигацию, поэтому нужно подписаться на событие nav.
document.addEventListener("nav", mountForms)
mountForms()Именно так форма “перерисовывается” при переходах без перезагрузки.
3. Добавьте стили
Файл: quartz/components/styles/feedbackForm.scss
Здесь описываются:
- карточка формы
- поля ввода
- группы чекбоксов/радио
- кнопки и статусы
SCSS подключается через FeedbackForm.css = style.
4. Экспортируйте компонент
Файл: quartz/components/index.ts
import FeedbackForm from "./FeedbackForm"
export {
// ...
FeedbackForm,
}5. Подключите компонент в layout
Файл: quartz.layout.ts
Чтобы скрипт и стили работали на всех страницах:
export const sharedPageComponents: SharedLayout = {
// ...
afterBody: [Component.ServicesCarousel(), Component.FeedbackForm()],
}Если нужен компонент только на отдельных страницах — можно добавить его в конкретный layout или через ConditionalRender.
6. Используйте компонент в контенте
Quartz не всегда корректно обрабатывает кастомный HTML-тег, поэтому используем div:
<div class="feedback-form" data-source="/static/data/feedback-form.json"></div>Это надежный способ, который гарантированно не экранируется в Markdown.
7. Настройка через JSON
Конфиг лежит в quartz/static/data/ и подтягивается через data-source.
Пример:
{
"title": "Форма обратной связи",
"subtitle": "Коротко, по делу. Я свяжусь с вами.",
"action": "https://app.slavx.ru/api/v1/f/12a7dd50d5c0",
"method": "POST",
"submitLabel": "Отправить",
"privacyNote": "Без спама",
"fields": [
{
"type": "text",
"name": "name",
"placeholder": "Имя / компания",
"required": true,
"layout": "two-columns"
},
{ "type": "email", "name": "email", "placeholder": "Почта", "required": true },
{
"type": "textarea",
"name": "message",
"placeholder": "Сообщение",
"required": true,
"rows": 5
}
]
}Форма отправляет JSON-пayload и показывает статус прямо в карточке.
8. Разъяснения полей (JSON)
Ниже — расшифровка параметров для fields[]:
type— тип поля:text,email,tel,textarea,select,checkbox,radio,range,hidden.name— ключ, под которым поле уйдет в JSON-payload; обязателен для всех типов, иначе значение не попадет в отправку.label— текст подписи (дляcheckbox/radioэто заголовок группы).placeholder— подсказка внутри поля (дляtext,email,tel,textarea).required— делает поле обязательным; для группы чекбоксов требуется выбрать минимум один.value— значение по умолчанию; важно дляhiddenи одиночногоcheckbox.rows— высотаtextarea.options— варианты дляselect,checkbox,radio(каждый вариант:label,value,checked).multiple— включает множественный выбор дляselect.autocomplete— HTML-атрибут автозаполнения.help— вспомогательный текст под полем.min— минимальное значение дляrange.max— максимальное значение дляrange.step— шаг изменения дляrange.unit— подпись справа от значения (например,₽).showValue— показывать значение уrange(по умолчаниюtrue).layout— специальный макет,two-columnsавтоматически группирует указанные поля в два столбца (подходит для контактных данных).
9. Поддерживаемые поля
text, email, tel, textarea, select, checkbox, radio, range, hidden.
Пример с выпадающим списком и чекбоксами:
{
"fields": [
{
"type": "select",
"name": "service",
"label": "Услуга",
"required": true,
"options": [
{ "label": "Аудит", "value": "audit" },
{ "label": "Разработка", "value": "dev" }
]
},
{
"type": "checkbox",
"name": "channels",
"label": "Удобный канал",
"required": true,
"options": [
{ "label": "Telegram", "value": "telegram" },
{ "label": "Email", "value": "email" }
]
},
{
"type": "radio",
"name": "budget",
"label": "Бюджет",
"required": true,
"options": [
{ "label": "до 50k", "value": "50k" },
{ "label": "50–200k", "value": "200k" }
]
}
]
}Пример со слайдером бюджета:
{
"fields": [
{
"type": "range",
"name": "budget",
"label": "Бюджет, ₽",
"min": 50000,
"max": 500000,
"step": 25000,
"value": 150000,
"unit": "₽"
}
]
}Пример двухколоночной раскладки для контактных данных:
{
"fields": [
{
"type": "text",
"name": "name",
"label": "Имя",
"placeholder": "Как к вам обращаться",
"required": true,
"layout": "two-columns"
},
{
"type": "text",
"name": "company",
"label": "Компания",
"placeholder": "Название компании",
"layout": "two-columns"
},
{
"type": "tel",
"name": "phone",
"label": "Телефон",
"placeholder": "+7...",
"layout": "two-columns"
},
{
"type": "email",
"name": "email",
"label": "Email",
"placeholder": "you@example.com",
"required": true,
"layout": "two-columns"
}
]
}10. Кнопки и дополнительные действия
Можно добавить свои кнопки и ссылки:
{
"actions": [
{ "type": "submit", "label": "Отправить" },
{
"type": "link",
"label": "Написать в Telegram",
"href": "https://t.me/your_handle",
"variant": "secondary"
}
]
}11. Что отправляется на сервер
Форма отправляет JSON:
select→ строка (или массив приmultiple)checkbox→ массив выбранных значенийradio→ выбранное значениеhidden→ статическое значениеtext/email/tel/textarea→ строка
Если action не задан — выводится ошибка. При успехе форма очищается.
Итог
Так вы получаете компонент, который можно вставить на любую страницу, а его поведение настраивать только через JSON — без изменения кода.
layout— не обязательная подсказка рендереру,two-columnsпомещает поля в двухколоночный контейнер (подходит для контактных данных).