/message-editor

Компонент поля ввода сообщения для веб-версии

Primary LanguageTypeScript

Текстовый редактор для ТамТам

Минималистичный текстовый редактор и парсер текста, оптимизированный для небольших (5—10 КБ) сообщений.

ДЕМО

Основные возможности:

  • Парсинг эмоджи ☺️ и текстовых смайлов :)
  • Парсинг e-mail и ссылок с учётом структуры предложения. Например, в «Ты был на tamtam.chat?» и «Заходи на tamtam.chat?a=b» правильно определяет принадлежность ? ссылке.
  • Частичное соответствие RFC1738 для парсинга ссылок, можно использовать как валидатор URL.
  • Префиксные токены: @mention, #hashtag, /command.
  • Форматирование фрагментов текста (жирный, курсив, и т.д.).
  • Ограниченная поддержка Markdown.
  • Маленький: весь редактор весит всего 18 КБ (min/brotli).
  • Быстрый: написан с учётом особенностей оптимизаций современных JS VM для мгновенной обработки текста.

Установка

Так как собранный npm-пакет хранится в GitHub, для установки редактора сначала необходимо сделать файл .npmrc в основной папке вашего проекта и добавить туда следующую строку:

@tamtam-chat:registry=https://npm.pkg.github.com

Затем выполняем стандартную установку:

npm install @tamtam-chat/message-editor

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

Модуль можно использовать в качестве полноценного текстового редактора небольших сообщений, а также в качестве парсера статического текста.

Использование в качестве редактора

Для подключения редактора надо сделать следующее:

import { Editor } from '@tamtam-chat/message-editor';

// Указатель на контейнер, который нужно сделать редактируемым
const elem = document.getElementById('editor');

const editor = new Editor(elem, {
    value: 'Hello world! 😇'
});

// Можно слушать события на изменение выделения и текста редактора
editor.on('editor-selectionchange', () => {
    console.log('selection changed:', editor.getSelection())
});

editor.on('editor-update', () => {
    console.log('content updated:', editor.model)
});

Класс Editor создаёт JS-обёртку над переданным DOM-элементом и добавляет ему функциональность редактора. С помощью полученного объекта можно управлять содержимым редактора, прослушивать события жизненного цикла и т.д.

Вторым аргументом в конструктор Editor можно передать объект с опциями редактора:

  • value — значение редактора по умолчанию.
  • parse — параметры для парсера (см. ниже).
  • shortcuts — объект, где ключом является сочетание клавиш, а значением – функция, которая выполняет действие на вызов указанного сочетания.
  • resetFormatOnNewline — если true, при вставке перевода строки будет автоматически сбрасываться форматирование.
  • emoji — функция отрисовки эмоджи.

Пример:

const editor = new Editor(document.getElementById('editor'), {
    value: 'Hello world!',
    shortcuts: {
        'Cmd-Shift-B': (ed, evt) => {
            ed.setValue('test');
        }
    }
})

Шорткаты (клавиатурные сочетания)

В редактор встроен механизм для обработки клавиатурных сочетаний. Как правило, это какой-то печатный символ и модификаторы: Shift, Alt, Ctrl, Meta, Cmd.

Если используется модификатор Cmd, на macOS он будет означать клавишу Command ⌘, а на Windows/Linux — Ctrl.

Пример допустимых сочетаний: Cmd-Alt-V, Ctrl+B, Enter.

Использование в качестве парсера

Модуль также можно использовать и в качестве парсера статического текста: например, если в тексте вам нужно разметить эмоджи и/или ссылки. Так как кодовая база оптимизирована для tree-shaking, при использовании модуля как парсера, код редактора не будет добавлен в финальный бандл.

import { parse } from '@tamtam-chat/message-editor';

const tokens = parse('test 🥳 tamtam.chat');

Функция возвращает плоский массив токенов (см. ниже описание модели парсера). В качестве второго аргумента можно передать объект со следующими параметрами парсера:

  • markdown — использовать markdown-символы для разметки. На данный момент поддерживаются следующие конструкции: *жирный*, _курсив_, ~перечёркнутый~, `код`, [текст ссылки](http://example.com).
  • textEmoji — находить текстовые смайлы в указанной строке и добавить им эмоджи-представления.
  • hashtag — размечать #hashtags.
  • mention — размечать @mention.
  • command — размечать /command.
  • link — находить и размечать ссылки в тексте.

Модель данных

Модель, в котором хранится внутреннее представление текста, представляет из себя плоский массив токенов. Токен – это единица данных текста. Объект, который содержит как минимум следующие поля:

  • type (string): тип токена.
  • value (string): значение токена, фрагмент оригинальной строки.
  • format (number): битовая маска с текущим форматированием токена.
  • emoji (array): опциональное свойство, если не пустое — это означает, что в value есть эмоджи или текстовые смайлы. Массив объектов вида { from: 1, to: 3 } где поля from и to указывают на диапазон символа эмоджи внутри value. Если в этом объекте содержится поле emoji — значит, диапазон указывает на текстовый смайл, а поле emoji содержит его эмоджи-представление.

Если склеить все значения поля value массива токенов, мы получим оригинальную строку:

import { parse } from '@tamtam-chat/message-editor';

const text = 'a :) 😇 b'
const tokens = parse(text, { textEmoji: true });
console.log(text === tokens.map(t => t.value).join('')); // true

Таким образом, поле value всегда содержит оригинальный текст, а замена эмоджи и текстовых смайлов выполняется только при отрисовке.

В зависимости от типа type, в объекте токена могут содержаться дополнительные свойства:

  • text — обычный текстовый токен.
  • link — ссылка, абсолютный URL содержится в поле link. Также есть поле auto, которое указывает, что ссылка была автоматически разпознана в тексте (true) или пользователь указал ссылку самостоятельно (false).
  • mention – упоминание вида @username.
  • hashtag – хэштэг вида #hashtag.
  • command – команда вида /command.

События редактора

Все события работают как обычные DOM-события на указанном элементе-контейнере. На них можно подписаться как через стандартный elem.addEventListener(eventName, handler), так и через вспомогательную функцию editor.on(eventName, handler) редактора.

  • editor-selectionchange — изменилась позиция курсора или выделение в редакторе.
  • editor-formatchange — изменилось форматирование на фрагменте текста.
  • editor-update — изменилось содержимое редактора.