npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@brandup/ui-richeditor

v1.0.36

Published

Rich text editor over a contenteditable element (bold, italic, strike, underline).

Readme

@brandup/ui-richeditor

Build Status

Редактор текста на базе contenteditable. Получает в конструкторе элемент, делает его редактируемым и берёт на себя всю работу с вводом: форматирование (жирный, курсив, зачёркивание, подчёркивание), панель инструментов, режим набора, нормализацию пробелов и сериализацию значения в HTML или Markdown.

Реализован на чистом Selection/Range API — без устаревшего document.execCommand, поэтому разметка всегда семантическая и предсказуема между браузерами.

Установка

npm i @brandup/ui-richeditor

Использование

import RichEditor from "@brandup/ui-richeditor";

const elem = document.getElementById("editor") as HTMLElement;
const editor = new RichEditor(elem, {
    format: true,
    tools: ["bold", "italic", "strike", "underline"],
    storage: "html", // или "markdown"
    placeholder: "Введите текст",
    multiline: true,
});

editor.onChange(({ value }) => console.log(value));

Конструктор оборачивает переданный элемент в div.ui-richeditor (к нему привязан UIElement) и делает элемент редактируемым (div.ui-richeditor-input).

Панель форматирования

Панель — общая для всех редакторов (div.ui-richeditor-toolbar). При фокусе редактора она перестраивается под его инструменты, позиционируется над ним и показывается; при потере фокуса — скрывается. Кнопки диспатчат форматирование напрямую активному редактору.

По умолчанию панель живёт в document.body (position: fixed) — это защищает её от обрезки overflow: hidden у родителей. Если задан toolbarContainer, панель монтируется в него и позиционируется относительно него (position: absolute, над контейнером) — например, TextBox передаёт свой контейнер .ui-textbox.

Опции (RichEditorOptions)

| Опция | Тип | Описание | |---|---|---| | format | boolean | Включает форматирование и панель инструментов | | tools | FormatTool[] | Состав инструментов (по умолчанию все) | | storage | "html" \| "markdown" | Формат сериализации значения (по умолчанию html) | | markers | Partial<FormatMarkers> | Переопределение markdown-маркеров по инструментам | | placeholder | string \| null | Текст-заглушка | | multiline | boolean | Многострочный режим | | readonly | boolean | Только для чтения — запрещает ввод и изменение текста (выделение и копирование остаются) | | toolbarContainer | HTMLElement \| null | Контейнер для панели; по умолчанию document.body (position: fixed). Если задан — панель монтируется в него и позиционируется над ним (position: absolute). Контейнер должен быть position: relative | | value | string | Начальное значение | | filterChar | (char) => boolean | Хук: false — отклонить вводимый символ | | filterPaste | (text) => string \| null | Хук: null — отклонить вставку; иначе очищенный текст | | onReject | () => void | Хук: ввод отклонён (символ/вставка) | | onEnter | () => void | Хук: Enter в однострочном режиме |

Хуки filterChar/filterPaste/onReject/onEnter позволяют хосту (например, @brandup/ui-textbox) накладывать собственные ограничения — фильтрацию по типу, submit формы, индикацию ошибки — не вмешиваясь в работу редактора.

API

| Член | Описание | |---|---| | editable | Редактируемый элемент | | format, formatTools, formatStorage, formatMarkers, multiline | Параметры экземпляра | | getValue(): string | Сериализованное значение (по storage) | | setValue(value: string): void | Установить значение (нормализует, генерирует change) | | getLength(): number | Длина текста (без учёта переводов строк) | | focus(): void | Установить фокус | | onChange(handler) | Подписка на событие richeditor-change | | destroy(): void | Разворачивает элемент обратно и освобождает ресурсы |

Поведение форматирования

  • Формат — переключатель (toggle): повторное применение снимает его.
  • Применяется к слову целиком: курсор внутри слова или выделение его части → формат охватывает всё слово; исходное выделение/каретка сохраняются.
  • Режим набора: на пустом месте (между пробелами / в пустом поле) кнопка/хоткей включают «ожидающий» формат — он применится к следующему введённому тексту. Сбрасывается при перемещении каретки, клике или потере фокуса.
  • Хоткеи Ctrl/Cmd+B/I/U. Зачёркивание — только кнопкой.
  • Отмена/повтор: Ctrl/Cmd+Z — отмена, Ctrl+Y или Ctrl/Cmd+Shift+Z — повтор. История форматирования, абзацев, переносов и печати ведётся редактором (нативный undo не видит ручных DOM-правок), поэтому доступна только при включённом форматировании (format: true). Печать коалесится в один шаг отмены по паузе ~300 мс; глубина истории — 100 шагов.
  • При потере фокуса и после setValue пробелы нормализуются (схлопывание повторов + обрезка краёв строк).

Многострочный режим: абзацы и переносы

При multiline: true контент структурируется по абзацам:

  • Enter → новый абзац (<p>);
  • Shift+Enter или Ctrl/Cmd+Enter → мягкий перенос (<br>) внутри абзаца;
  • блуждающий текст и <div> нормализуются в <p> при вводе.

Хвостовой перенос абзаца отбрасывается (это <br>-заполнитель); пустая строка делается отдельным абзацем.

При нормализации (потеря фокуса, setValue, инициализация) пустые абзацы удаляются.

Вставка форматированного текста

При включённом форматировании вставка (paste) сохраняет форматирование из буфера обмена (text/html):

  • разметка санитизируется до включённых инструментов (синонимы STRONG/EM/DEL/INS → канонические b/i/s/u, всё прочее — span, стили, классы, <style>/<script> — отбрасывается, текст сохраняется);
  • multiline сохраняет абзацы <p> и мягкие переносы <br>, разбивая текущий абзац по каретке; single-line — инлайн, абзацы/переносы становятся пробелами;
  • если text/html нет — простая текстовая вставка (как раньше);
  • хук filterPaste остаётся в силе: вернул null — вставка отклоняется; изменил текст (обрезка по длине, фильтр по типу) — форматирование не сохраняется, вставляется очищенный текст.

Вся вставка — один шаг истории (Ctrl+Z откатывает целиком).

Формат хранения

| storage | Хранение | Абзац / мягкий перенос | Маркеры форматирования | |---|---|---|---| | html | Санитизированный HTML | <p>…</p> / <br> | <b>, <i>, <s>, <u> | | markdown | Лёгкая разметка | \n\n / \n | **жирный**, *курсив*, ~~зачёркнутый~~, ++подчёркнутый++ |

Без форматирования (plain) значение хранится как markdown без инструментов: абзацы \n\n, мягкий перенос \n.

Маркеры markdown настраиваются через markers. При разборе применяются по убыванию длины, поэтому длинный маркер срабатывает раньше короткого-префикса. Глубоко вложенные комбинации гарантированно сохраняются только в режиме html.

CSS

Подключается richeditor.less. Цветовые переменные --input-* берутся из @brandup/ui-kit (с fallback-значениями для standalone). Классы: focused на обёртке (поле в фокусе), visible на общей панели (показана), active на кнопке инструмента (формат активен на выделении).