fix search

This commit is contained in:
fantasticit 2022-11-25 14:55:16 +08:00
parent ba189b26e2
commit ba842033c8
2 changed files with 58 additions and 46 deletions

View File

@ -8,21 +8,9 @@ import { Editor } from 'tiptap/core';
declare module '@tiptap/core' { declare module '@tiptap/core' {
interface Commands<ReturnType> { interface Commands<ReturnType> {
search: { search: {
/**
* @description Set search term in extension.
*/
setSearchTerm: (searchTerm: string) => ReturnType; setSearchTerm: (searchTerm: string) => ReturnType;
/**
* @description Set replace term in extension.
*/
setReplaceTerm: (replaceTerm: string) => ReturnType; setReplaceTerm: (replaceTerm: string) => ReturnType;
/**
* @description Replace first instance of search result with given replace term.
*/
replace: () => ReturnType; replace: () => ReturnType;
/**
* @description Replace all instances of search result with given replace term.
*/
replaceAll: () => ReturnType; replaceAll: () => ReturnType;
goToPrevSearchResult: () => void; goToPrevSearchResult: () => void;
goToNextSearchResult: () => void; goToNextSearchResult: () => void;
@ -35,18 +23,6 @@ interface Result {
to: number; to: number;
} }
interface SearchOptions {
searchTerm: string;
replaceTerm: string;
results: Result[];
currentIndex: number;
searchResultClass: string;
searchResultCurrentClass: string;
caseSensitive: boolean;
disableRegex: boolean;
onChange?: () => void;
}
interface TextNodesWithPosition { interface TextNodesWithPosition {
text: string; text: string;
pos: number; pos: number;
@ -206,8 +182,23 @@ const gotoSearchResult = ({ view, tr, searchResults, searchResultCurrentClass, g
export const ON_SEARCH_RESULTS = 'ON_SEARCH_RESULTS'; export const ON_SEARCH_RESULTS = 'ON_SEARCH_RESULTS';
interface SearchOptions {
searchTerm: string;
replaceTerm: string;
searchResultClass: string;
searchResultCurrentClass: string;
caseSensitive: boolean;
disableRegex: boolean;
onChange?: () => void;
}
interface SearchStorage {
results: Result[];
currentIndex: number;
}
// eslint-disable-next-line @typescript-eslint/ban-types // eslint-disable-next-line @typescript-eslint/ban-types
export const SearchNReplace = Extension.create<SearchOptions>({ export const SearchNReplace = Extension.create<SearchOptions, SearchStorage>({
name: 'search', name: 'search',
addOptions() { addOptions() {
@ -224,14 +215,21 @@ export const SearchNReplace = Extension.create<SearchOptions>({
}; };
}, },
addStorage() {
return {
results: [],
currentIndex: -1,
};
},
addCommands() { addCommands() {
return { return {
setSearchTerm: setSearchTerm:
(searchTerm: string) => (searchTerm: string) =>
({ state, dispatch, editor }) => { ({ state, dispatch, editor }) => {
this.options.searchTerm = searchTerm; this.options.searchTerm = searchTerm;
this.options.results = []; this.storage.results = [];
this.options.currentIndex = 0; this.storage.currentIndex = 0;
(editor as Editor).eventEmitter && (editor as Editor).eventEmitter.emit(ON_SEARCH_RESULTS); (editor as Editor).eventEmitter && (editor as Editor).eventEmitter.emit(ON_SEARCH_RESULTS);
updateView(state, dispatch); updateView(state, dispatch);
return false; return false;
@ -247,31 +245,36 @@ export const SearchNReplace = Extension.create<SearchOptions>({
}, },
replace: replace:
() => () =>
({ state, dispatch }) => { ({ state, dispatch, editor }) => {
const { replaceTerm, results, currentIndex } = this.options; const { replaceTerm } = this.options;
const { currentIndex, results } = this.storage;
const currentResult = results[currentIndex]; const currentResult = results[currentIndex];
if (currentResult) { if (currentResult) {
replace(replaceTerm, [currentResult], { state, dispatch }); replace(replaceTerm, [currentResult], { state, dispatch });
this.options.results.splice(currentIndex, 1); this.storage.results.splice(currentIndex, 1);
} else { } else {
replace(replaceTerm, results, { state, dispatch }); replace(replaceTerm, results, { state, dispatch });
this.storage.results.shift();
this.options.results.shift();
} }
(editor as Editor).eventEmitter && (editor as Editor).eventEmitter.emit(ON_SEARCH_RESULTS);
updateView(state, dispatch); updateView(state, dispatch);
return false; return false;
}, },
replaceAll: replaceAll:
() => () =>
({ state, tr, dispatch }) => { ({ state, tr, dispatch, editor }) => {
const { replaceTerm, results } = this.options; const { replaceTerm } = this.options;
const { results } = this.storage;
replaceAll(replaceTerm, results, { tr, dispatch }); replaceAll(replaceTerm, results, { tr, dispatch });
this.options.results = []; this.storage.currentIndex = -1;
this.storage.results = [];
(editor as Editor).eventEmitter && (editor as Editor).eventEmitter.emit(ON_SEARCH_RESULTS);
updateView(state, dispatch); updateView(state, dispatch);
@ -279,11 +282,12 @@ export const SearchNReplace = Extension.create<SearchOptions>({
}, },
goToPrevSearchResult: goToPrevSearchResult:
() => () =>
({ view, tr }) => { ({ view, tr, editor }) => {
const { currentIndex, results, searchResultCurrentClass } = this.options; const { searchResultCurrentClass } = this.options;
const { currentIndex, results } = this.storage;
const nextIndex = (currentIndex + results.length - 1) % results.length; const nextIndex = (currentIndex + results.length - 1) % results.length;
this.options.currentIndex = nextIndex; this.storage.currentIndex = nextIndex;
this.options.onChange && this.options.onChange(); (editor as Editor).eventEmitter && (editor as Editor).eventEmitter.emit(ON_SEARCH_RESULTS);
return gotoSearchResult({ return gotoSearchResult({
view, view,
tr, tr,
@ -294,11 +298,13 @@ export const SearchNReplace = Extension.create<SearchOptions>({
}, },
goToNextSearchResult: goToNextSearchResult:
() => () =>
({ view, tr }) => { ({ view, tr, editor }) => {
const { currentIndex, results, searchResultCurrentClass } = this.options; const { searchResultCurrentClass } = this.options;
const { currentIndex, results } = this.storage;
const nextIndex = (currentIndex + 1) % results.length; const nextIndex = (currentIndex + 1) % results.length;
this.options.currentIndex = nextIndex; this.storage.currentIndex = nextIndex;
this.options.onChange && this.options.onChange(); this.options.onChange && this.options.onChange();
(editor as Editor).eventEmitter && (editor as Editor).eventEmitter.emit(ON_SEARCH_RESULTS);
return gotoSearchResult({ return gotoSearchResult({
view, view,
tr, tr,
@ -334,7 +340,10 @@ export const SearchNReplace = Extension.create<SearchOptions>({
regex(searchTerm, disableRegex, caseSensitive), regex(searchTerm, disableRegex, caseSensitive),
searchResultClass searchResultClass
); );
extensionThis.options.results = results; extensionThis.storage.results = results;
if (extensionThis.storage.currentIndex > results.length - 1) {
extensionThis.storage.currentIndex = 0;
}
editor.eventEmitter && editor.eventEmitter.emit(ON_SEARCH_RESULTS); editor.eventEmitter && editor.eventEmitter.emit(ON_SEARCH_RESULTS);
if (ctx.getMeta('directDecoration')) { if (ctx.getMeta('directDecoration')) {
const { fromPos, toPos, attrs } = ctx.getMeta('directDecoration'); const { fromPos, toPos, attrs } = ctx.getMeta('directDecoration');

View File

@ -32,8 +32,11 @@ export const Search: React.FC<{ editor: Editor }> = ({ editor }) => {
setReplaceValue(''); setReplaceValue('');
setCurrentIndex(-1); setCurrentIndex(-1);
setResults([]); setResults([]);
editor.commands.setSearchTerm('');
editor.commands.setReplaceTerm('');
} }
}, [visible]); }, [editor, visible]);
useEffect(() => { useEffect(() => {
if (!visible) return; if (!visible) return;
@ -59,8 +62,8 @@ export const Search: React.FC<{ editor: Editor }> = ({ editor }) => {
const listener = () => { const listener = () => {
if (!visible) return; if (!visible) return;
const currentIndex = searchExtension ? searchExtension.options.currentIndex : -1; const currentIndex = searchExtension ? searchExtension.storage.currentIndex : -1;
const results = searchExtension ? searchExtension.options.results : []; const results = searchExtension ? searchExtension.storage.results : [];
setCurrentIndex((preIndex) => (preIndex !== currentIndex ? currentIndex : preIndex)); setCurrentIndex((preIndex) => (preIndex !== currentIndex ? currentIndex : preIndex));
setResults((prevResults) => (deepEqual(prevResults, results) ? prevResults : results)); setResults((prevResults) => (deepEqual(prevResults, results) ? prevResults : results));
}; };