3.7.1. Диалог спецсимволов

New in version 1.27.0-ms14.

3.7.1.1. Назначение

React-компонент SpecialSymbols, который оборачивает trigger-элемент (кнопку/иконку) и открывает панель выбора спецсимволов согласно параметру mode (см. раздел «Открытие»). children могут включать триггер и связанные элементы, но обязательным остаётся triggerRef. Цели вставки (targetRef/editor/onSymbolInsert) задаются параметрами и могут быть как внутри, так и вне обёртки SpecialSymbols.

Диалог спецсимволов

3.7.1.2. Параметры

Доступные параметры (props) компонента:

Параметр

Тип

По умолчанию

Описание

mode

'mini' | 'full' | 'both'

'both'

Режим открытия компонента (см. «Открытие»).

miniSlots

number

12

Количество слотов в mini-поповере для «Недавних».

triggerRef

React.RefObject<HTMLElement>

не задано

Ref на интерактивный DOM-элемент триггера.

onSymbolInsert

(symbol) => void

не задано

Внешний обработчик вставки (задаётся хостом).

editor

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

не задано

Инстанс редактора для вставки символа.

targetRef

React.RefObject<HTMLInputElement | HTMLTextAreaElement>

не задано

Целевое поле ввода (input/textarea).

3.7.1.3. Вставка символа

Компонент поддерживает три способа вставки. Если передано несколько параметров одновременно, используется только один — самый приоритетный.

Приоритеты вставки

Символ вставляется в первую доступную цель согласно списку (порядок задания пропсов не важен):

  1. onSymbolInsert(symbol) — внешний обработчик (логику вставки полностью берет на себя хост).

  2. editor — инстанс редактора форматированного текста.

  3. targetRef — прямая ссылка на input или textarea элемент.

Работа с несколькими полями

targetRef всегда указывает на одно текущее поле ввода. Если на странице несколько input/textarea, хост должен поддерживать targetRef в актуальном состоянии, передавая ссылку на активный в данный момент элемент ввода.

Tip

Рекомендуемый паттерн

Чтобы символ всегда попадал в нужное поле, параметр targetRef должен обновляться динамически. В каждом целевом поле (input/textarea) добавьте обработчик onFocus, который будет перезаписывать ref, передаваемый в компонент.

3.7.1.4. Основные UX-сценарии и поведение

3.7.1.4.1. Открытие

Компонент работает в одном из режимов, заданных параметром mode. Открытие инициируется взаимодействием с триггером (ЛКМ или Enter при фокусе на триггере):

  • mode='mini': открывается mini-поповер.

  • mode='full': открывается full-диалог.

  • mode='both': открывается mini-поповер, из него доступен переход в full (mini закрывается).

3.7.1.4.2. Закрытие

Компонент закрывается в зависимости от активного режима:

mini-поповер

Закрывается при:

  • Нажатии клавиши Escape.

  • Клике вне поповера.

  • Выборе символа (одиночный клик, Enter или Space) — символ вставляется, поповер закрывается.

  • Переходе в full-диалог (режим mode='both') — mini закрывается.

full-диалог

Закрывается при:

  • Нажатии клавиши Escape: если поле фильтрации не пусто - очищает фильтр; если фильтр пуст - закрывает диалог.

  • Нажатии кнопки «Отмена».

  • Нажатии кнопки закрытия (крестик).

  • Клике вне диалога.

3.7.1.4.3. Mini-поповер

  • Показывает сетку «Недавних» (количество слотов задаётся miniSlots, по умолчанию 12; значение нормализуется в диапазон [1..48]). При выходе из диапазона значение приводится к ближайшей границе.

  • Управление фокусом:

    • При открытии с клавиатуры: фокус автоматически переводится на кнопку «Другие символы…» (если она доступна), иначе — на первый символ в сетке «Недавних».

    • При открытии мышью: фокус изначально не переводится и остаётся на элементе‑триггере. При первом нажатии Tab (или Shift+Tab) фокус перемещается внутрь панели на первый активный элемент.

    • Зацикливание: навигация с помощью Tab / Shift+Tab ограничена внутри поповера — после последнего элемента фокус снова возвращается к первому.

  • Навигация по Tab/Shift+Tab зациклена внутри поповера.

  • Кнопка «Другие символы…»:

    • отображается только в режиме mode='both'.

    • при нажатии открывает full-диалог и закрывает mini-поповер.

  • Выбор символа: - одиночный клик, Enter или Space вставляют символ и закрывают поповер.

3.7.1.4.4. Full-диалог

  • При открытии фокус ставится в поле фильтра.

  • Фильтрация:

    • результаты фильтра показываются после короткой задержки (debounce).

    • Escape: если фильтр не пуст — очищает фильтр, иначе закрывает диалог.

  • Вкладки групп:

    • клик по вкладке прокручивает к соответствующей секции.

    • если активна фильтрация — вкладка сначала очищает фильтр, затем выполняется прокрутка.

  • Выбор символа:

    • одиночный клик или Space — выбрать символ и показать превью (tooltip).

    • Enter или двойной клик ЛКМ — вставить символ.

  • Кнопки действий:

    • «Вставить» — вставляет выбранный символ в целевое поле/редактор (доступна только при выборе символа).

    • «Копировать» — копирует выбранный символ в буфер обмена (доступна только при выборе символа).

  • Фокус в модалке:

    • Tab/Shift+Tab не должны переводить фокус на элементы страницы «под» модалкой (фокус остаётся внутри диалога).

3.7.1.4.5. Недавние

  • «Недавние» хранятся в localStorage по ключу gs-recent-symbols.

  • Синхронизация между вкладками реализована через событие storage: когда в одной вкладке меняется localStorage, остальные вкладки того же origin получают window-событие storage и могут обновить состояние.

Note

Важно: вкладка, которая выполнила запись, событие storage не получает - для нее обновление делается напрямую.

  • Если сохранённых «Недавних» нет, UI использует дефолтный набор как fallback (он не записывается в localStorage).

3.7.1.4.6. Фокус после закрытия

После закрытия фокус возвращается туда, где пользователь продолжит работу:

  1. Редактор форматированного текста (если доступен и редактируемый).

  2. Иначе targetRef.

  3. Иначе триггер (triggerRef).

3.7.1.4.7. Глобальный индикатор

На время открытия (mini или full) компонент выставляет window.__gsSpecialSymbolsOpen = true/false.

Note

window.__gsSpecialSymbolsOpen — это boolean-флаг состояния: компонент выставляет true на время открытия mini/full и возвращает false при закрытии. Флаг хранится в window, чтобы его могли прочитать другие части приложения, не являющиеся потомками React-компонента.

3.7.1.5. Ограничения и ожидания

  • triggerRef обязателен: должен указывать на интерактивный DOM-элемент, который может получать фокус и активироваться пользователем (click, Enter, Space). Иначе не будет корректной работы с клавиатурой.

  • Должен быть задан хотя бы один путь вставки: onSymbolInsert или editor или targetRef.

  • Для контролируемых полей (React state) предпочтительнее onSymbolInsert: иначе внешнее состояние может «перезаписать» изменения в targetRef.

3.7.1.6. Чек-лист

  • Передан triggerRef и он установлен на DOM-элемент триггера.

  • Задан один из путей вставки (onSymbolInsert / editor / targetRef).

  • Выбран mode (mini / full / both) под нужный сценарий.