@shabalinmax/tiptap-scout
v0.6.0
Published
Search and replace extension for Tiptap
Maintainers
Readme
@shabalinmax/tiptap-scout
Расширение поиска и замены для Tiptap 3.
Существующие аналоги устарели и написаны под Tiptap 2. Этот пакет разработан для Tiptap 3 с нуля.
Live Demo | GitHub | README in English
Возможности
- Поиск текста с опциональным учётом регистра
- Режим поиска целых слов
- Сохранение регистра при замене (например, «Привет» → «Мир», «привет» → «мир»)
- Поиск корректно работает через inline-форматирование (жирный, курсив, ссылки и т.д.)
- Совпадение не может пройти сквозь перенос строки, картинку и другие inline-ноды — замена не удалит их молча
- Подсветка совпадений через ProseMirror Decorations
findNext/findPreviousс циклической навигациейreplace/replaceAllс корректным undo/redo- Опциональный scroll к текущему совпадению
- Live update — автоматический пересчёт поиска при изменении документа
- Счётчик "N из M" через
editor.storage.scout - Настраиваемые CSS-классы — стили не навязываются
- React hook
useScoutдля реактивного состояния - Полная типизация TypeScript с автокомплитом команд
Установка
npm install @shabalinmax/tiptap-scoutPeer-зависимости
npm install @tiptap/core @tiptap/pmИспользование
import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'
import { Scout } from '@shabalinmax/tiptap-scout'
const editor = new Editor({
extensions: [
StarterKit,
Scout.configure({
searchResultClass: 'search-highlight',
currentResultClass: 'search-highlight-current',
scrollIntoView: true,
liveUpdate: true,
}),
],
})
// Поиск
editor.commands.find('привет')
// Навигация между совпадениями
editor.commands.findNext()
editor.commands.findPrevious()
// Замена
editor.commands.replace('мир')
editor.commands.replaceAll('мир')
// Опции поиска
editor.commands.setCaseSensitive(true)
editor.commands.setWholeWord(true)
// Очистить запрос, сохранив опции…
editor.commands.clearSearch()
// …или сбросить всё к дефолтам (например, при закрытии панели поиска)
editor.commands.resetSearch()
// Доступ к состоянию (например, для отображения "2 из 5")
const { searchTerm, results, currentIndex } = editor.storage.scout
console.log(`${currentIndex + 1} из ${results.length}`)CSS
Расширение не включает стили. Добавьте свои:
.search-highlight {
background-color: yellow;
}
.search-highlight-current {
background-color: orange;
}React
import { useState } from 'react'
import { Scout } from '@shabalinmax/tiptap-scout'
import { useScout } from '@shabalinmax/tiptap-scout/react'
function SearchBar({ editor }) {
const [replaceValue, setReplaceValue] = useState('')
const {
searchTerm,
currentIndex,
totalCount,
find,
findNext,
findPrevious,
replace,
replaceAll,
clearSearch,
} = useScout(editor)
return (
<div>
<input value={searchTerm} onChange={(e) => find(e.target.value)} />
<span>{totalCount > 0 ? `${currentIndex + 1} из ${totalCount}` : 'Нет результатов'}</span>
<button onClick={findPrevious}>Назад</button>
<button onClick={findNext}>Далее</button>
<input value={replaceValue} onChange={(e) => setReplaceValue(e.target.value)} />
<button onClick={() => replace(replaceValue)}>Заменить</button>
<button onClick={() => replaceAll(replaceValue)}>Заменить все</button>
<button onClick={clearSearch}>Сбросить</button>
</div>
)
}React — опциональная peer-зависимость, не требуется для проектов без React.
Опции
| Опция | Тип | По умолчанию | Описание |
| --- | --- | --- | --- |
| searchResultClass | string | 'scout-result' | CSS-класс для всех совпадений |
| currentResultClass | string | 'scout-result-current' | CSS-класс для текущего совпадения |
| scrollIntoView | boolean | false | Прокрутка к текущему совпадению при навигации |
| liveUpdate | boolean | false | Автоматический пересчёт поиска при изменении документа |
Команды
| Команда | Параметры | Описание |
| --- | --- | --- |
| find | searchTerm: string | Поиск текста |
| findNext | — | Перейти к следующему совпадению (циклически) |
| findPrevious | — | Перейти к предыдущему совпадению (циклически) |
| replace | replaceWith: string | Заменить текущее совпадение |
| replaceAll | replaceWith: string | Заменить все совпадения (один шаг undo) |
| setCaseSensitive | value: boolean | Включить/выключить учёт регистра при поиске |
| setWholeWord | value: boolean | Включить/выключить поиск целых слов |
| setPreserveCase | value: boolean | Включить/выключить сохранение регистра при замене |
| clearSearch | — | Сброс результатов и декораций (опции поиска сохраняются) |
| resetSearch | — | Сброс поиска и всех опций к дефолтам |
Storage (editor.storage.scout)
| Поле | Тип | Описание |
| --- | --- | --- |
| searchTerm | string | Текущий поисковый запрос |
| results | SearchResult[] | Массив позиций { from, to } |
| currentIndex | number | Индекс текущего совпадения (с нуля) |
| caseSensitive | boolean | Учитывать ли регистр при поиске |
| wholeWord | boolean | Искать ли только целые слова |
| preserveCase | boolean | Сохранять ли регистр при замене |
Планы
- Режим поиска по регулярным выражениям
- Capture groups ($1, $2) в строке замены
- Поиск/замена внутри выделения
Лицензия
MIT
