Editor
Базовый сценарий
Абзацев: 0
Готовое значение
Только для чтения и отключение
Ошибка
Размеры
Минимальная высота
Плоский текст значения:
Внутри C3FormField
Заполните описание
Плоский текст: —
События
focus: false · blur: false
Тулбокс и типы блоков
Наведите на блок или нажмите Tab в пустом — откроется тулбокс. Стрелки и Enter выбирают тип, Escape закрывает.
Типы: paragraph
Список
Enter добавляет пункт, Backspace в начале пустого пункта заканчивает список.
Смена типа блока
Нажмите Tab в начале абзаца — откроется тулбокс. Текст переезжает вместе с блоком: абзац становится списком с пунктами, список — обратно абзацем с переводами строк, начертания и настройки сохраняются. В середине текста Tab ведёт себя как в обычном поле.
Блок:
Настройки типа блока
Наведите на блок с настройками: справа появится кнопка с шестерёнкой. У заголовка это уровень, у списка — нумерация, у кода — язык. Абзац и разделитель настроек не имеют.
Значение: [{"type":"header","data":{"text":"Заголовок с настройками","level":2}},{"type":"list","data":{"text":"","items":["Первый пункт","Второй пункт"],"style":"unordered"}},{"type":"raw","data":{"text":"const a = 1","language":"ts"}},{"type":"paragraph","data":{"text":"У абзаца настроек нет, поэтому кнопки у него нет."}}]
В режиме readOnly и disabled кнопки настроек нет: документ не должен меняться.
Начертания внутри текста
Выделите текст в абзаце, заголовке или пункте списка — над выделением появится панель. Ctrl+B — полужирный, Ctrl+I — курсив, Ctrl+E — моноширинный, Ctrl+K — ссылка. Повторное нажатие снимает начертание, ссылка открывает диалог адреса. В блоке кода форматирования нет.
Начертания в значении:
Ссылка
Выделите слово и нажмите Ctrl+K (или кнопку Ссылка в панели) — откроется диалог адреса с текущим адресом выделения. Пустое поле снимает ссылку, нерабочая схема (javascript:, data:) не применится. Ссылка хранится интервалом с адресом: { type: 'link', from, to, href }.
Начертания:
Вложенный список
Tab в начале пункта вкладывает его на уровень глубже, Shift+Tab выносит на уровень выше. В середине текста Tab ведёт себя как в обычном поле — уводит фокус. Глубина ограничена пятью уровнями.
Уровни: 0, 1, 1, 0, 1, 2, 0
Пункты в значении:
Входные параметры
| Имя | Тип | По умолчанию | Описание |
|---|---|---|---|
v-model | C3EditorValue | undefined | Документ редактора: { version, time, blocks: [{ id, type, data }] }. version — версия схемы для миграций, time — время последней правки. Пустое значение показывается как один пустой абзац. |
placeholder | string | undefined | Подсказка, пока документ пуст. |
size | 'sm' | 'md' | 'lg' | undefined | Размер поля: отступы и размер шрифта. Если не задан, берётся из C3FormField. |
minHeight | number | string | undefined | Минимальная высота области текста. Число трактуется как пиксели, строка передаётся в CSS как есть. |
readOnly | boolean | false | Запрещает правку, оставляет текст доступным для чтения, выделения и навигации по блокам. |
disabled | boolean | false | Отключает поле. |
autofocus | boolean | false | Ставит фокус в первый блок после монтирования. Не работает вместе с disabled. |
required | boolean | false | Маркер обязательности, добавляет aria-required. Учитывается и значение из C3FormField. |
error | boolean | false | Признак ошибки: красная рамка и aria-invalid. Ошибка из C3FormField имеет приоритет. |
name | string | undefined | Имя поля для нативной формы: документ уходит в скрытом input строкой JSON. |
tools | C3BlockTool[] | [paragraphTool] | Инструменты типов блоков: по ним строится реестр, порядок определяет порядок в тулбоксе. Контракт и правила — в src/runtime/utils/_editor_tools.ts, инструменты — в src/runtime/editor/tools/. По умолчанию: paragraph, header, quote, list, raw, delimiter. Инструменты без правки текста (разделитель, а позже картинка и таблица) объявляют editable: false. |
ariaLabel | string | undefined | Доступное имя группы. Если не задано, берётся подпись из C3FormField. |
Настройки типа блока (Tunes)
Набор полей объявляет инструмент в поле tunes, а рисует их редактор готовыми компонентами. Набор фиксированный: select, switch, number, text. Произвольный интерфейс настройки не рисуется.
- Кнопка настроек появляется у блока, если у его типа есть непустой
tunes, и скрыта в режимеreadOnlyиdisabled. - Диалог один на редактор и правит копию данных блока: пока Отмена не нажата, документ не меняется и история не пополняется.
- Значения приводятся к типам полей: числовой уровень заголовка остаётся числом, переключатель — булевым,
selectберёт значение из своих вариантов. - Значение вне списка вариантов и поля вне набора инструмента в данные не попадают.
- Применение попадает в историю:
Ctrl+Zвозвращает прежние значения. - У полей
numberграницыmin/maxудерживаются при записи.
События
update:modelValue— payloadC3EditorValue. Документ меняется на каждый ввод и на каждую структурную правку.focus— без payload. Блок получил фокус.blur— без payload. Редактор потерял фокус.
Описание
C3Editor — поле формы для структурированного текста: документ состоит из блоков, каждый блок — абзац. Подключается как любое другое поле: те же size, error, label, required, name и валидация, что у C3Input и C3Textarea.
Ядро ввода собственное, без внешних редакторов. Браузер не вставляет собственную разметку: Enter, Backspace на границе блока и вставка перехватываются, а правки идут через состояние документа. Каретка хранится как смещение в тексте блока, поэтому отмена возвращает ровно то состояние, которое было до правки.
План развития и набор инструментов — в docs/editor.md. Сейчас реализован первый этап: абзацы, ввод с клавиатуры, вставка из буфера обмена, история правок и работа в форме.
Дополнительная информация
Клавиатура
Enter— разделить блок по каретке. В пустом блоке блок удаляется, а каретка уходит в конец предыдущего. У спискаEnterдобавляет пункт, а не создаёт блок.Backspaceв начале блока — слить с предыдущим; в середине — обычное удаление символа.Deleteв конце блока — забрать следующий блок.Ctrl+ZиCtrl+Shift+ZлибоCtrl+Y— отмена и повтор. Правки набора одного блока склеиваются в один шаг.↑и↓— переход между блоками;HomeиEnd— края блока; сCtrl— края документа.Tab— увести фокус из редактора; в начале блока открывает тулбокс с типами; в начале пункта списка вкладывает его на уровень глубже. Между блоками перемещение только стрелками.Shift+Tab— уйти из редактора назад, в том числе из пустого блока с открытым тулбоксом; в начале пункта списка выносит его на уровень выше.
Начертания
- Хранятся интервалами в
data.marks:{ type: 'bold', from: 7, to: 10 }. Не HTML и не деревом токенов — каретка остаётся смещением в строке. - Формат версии 2. Документы версии 1 открываются как есть: отсутствие поля означает «без начертаний».
- У списка начертания лежат в пунктах, потому что текст разложен по ним; блок спрашивает инструмент через
readMarksиapplyMark. - Пересечение с выделением разрезает интервал, повторное применение снимает, а набор текста сдвигает интервалы вместе с текстом.
- Деление блока и склейка режут и сдвигают интервалы, поэтому форматирование переживает
EnterиBackspace. - Набор начертаний объявляет инструмент в поле
marks: у кода он пустой, и панель к нему не рисуется. - Ссылка хранит адрес в самом интервале:
{ type: 'link', from: 0, to: 5, href: 'https://…' }. Пустой адрес или нерабочая схема (javascript:,data:) снимают ссылку, а не открывают код. - Ссылку рисуют кнопкой панели или
Ctrl+K: открывается диалог ввода адреса. Повторное применение меняет адрес, пустое поле снимает ссылку, а края частично перекрытой ссылки сохраняют свой адрес.
Список и вложенность
- Пункт — это
{ text, level }, а данные хранятся плоско:levelу каждого пункта, дерево собирается при отрисовке. - Старый формат
items: string[]читается как пункты верхнего уровня, поэтому документы, сохранённые до появления вложенности, открываются как есть. Enterдобавляет пункт на уровне текущего, а не верхнего.Backspaceв начале пустого пункта удаляет его.- Уровень не перескакивает глубже открытого: пункту без родителя не на чем висеть, поэтому он остаётся там, где был.
- Потолок вложенности —
MAX_LEVEL(уровень 4, то есть пять уровней). Значение за границей приводится к нулю. - Отступ вложенного списка задаёт его уровень, а не глубина DOM: списки вложены друг в друга, и отступ от предка накапливался бы.
Вставка и IME
- Одиночный абзац вставляется в текущий блок по каретке, краевые пробелы сохраняются.
- Несколько абзацев вставляются отдельными блоками, текст после каретки становится последним из них.
- Форматирование из буфера обмена не переносится: используется только текст.
- Во время ввода через IME (китайский, японский, корейский) блоки не делятся и история не пополняется — правка фиксируется по окончании композиции.
Доступность
- Корень — группа с подписью из
ariaLabelили изC3FormField. - Каждый блок —
role="textbox"с названием типа блока для скринридера. - Активный блок доступен с клавиатуры (
tabindex="0"), остальные —-1:Tabпереходит между полями формы, стрелки — между блоками. - При ошибке добавляется
aria-invalid, при обязательности —aria-required, в режиме чтения —aria-readonly.
Прочее
- Слотов нет: содержимое формируют инструменты блоков.
- На сервере блоки отдаются только для чтения,
contenteditableпоявляется после монтирования — разметка детерминирована и совпадает при гидрации. - Чужая разметка, если её всё же вставил браузер, вычищается при потере фокуса: состояние документа остаётся источником правды.
- Стили только через
--c3-*, поэтому тёмная тема и RTL работают без отдельной поддержки. - Размер меняет пропорционально отступы и кегль, а высота следует за содержимым: фиксированной высоты у поля нет, нижнюю границу задаёт
minHeight. - Разметку блока рисует инструмент типа (
render), а данные читает черезsaveпри потере фокуса — попутно вычищая разметку, вставленную браузером. Невалидные данные помечаются, но не выбрасываются.