mirror of https://github.com/fantasticit/think.git
fix search
parent
ba189b26e2
commit
ba842033c8
|
@ -8,21 +8,9 @@ import { Editor } from 'tiptap/core';
|
|||
declare module '@tiptap/core' {
|
||||
interface Commands<ReturnType> {
|
||||
search: {
|
||||
/**
|
||||
* @description Set search term in extension.
|
||||
*/
|
||||
setSearchTerm: (searchTerm: string) => ReturnType;
|
||||
/**
|
||||
* @description Set replace term in extension.
|
||||
*/
|
||||
setReplaceTerm: (replaceTerm: string) => ReturnType;
|
||||
/**
|
||||
* @description Replace first instance of search result with given replace term.
|
||||
*/
|
||||
replace: () => ReturnType;
|
||||
/**
|
||||
* @description Replace all instances of search result with given replace term.
|
||||
*/
|
||||
replaceAll: () => ReturnType;
|
||||
goToPrevSearchResult: () => void;
|
||||
goToNextSearchResult: () => void;
|
||||
|
@ -35,18 +23,6 @@ interface Result {
|
|||
to: number;
|
||||
}
|
||||
|
||||
interface SearchOptions {
|
||||
searchTerm: string;
|
||||
replaceTerm: string;
|
||||
results: Result[];
|
||||
currentIndex: number;
|
||||
searchResultClass: string;
|
||||
searchResultCurrentClass: string;
|
||||
caseSensitive: boolean;
|
||||
disableRegex: boolean;
|
||||
onChange?: () => void;
|
||||
}
|
||||
|
||||
interface TextNodesWithPosition {
|
||||
text: string;
|
||||
pos: number;
|
||||
|
@ -206,8 +182,23 @@ const gotoSearchResult = ({ view, tr, searchResults, searchResultCurrentClass, g
|
|||
|
||||
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
|
||||
export const SearchNReplace = Extension.create<SearchOptions>({
|
||||
export const SearchNReplace = Extension.create<SearchOptions, SearchStorage>({
|
||||
name: 'search',
|
||||
|
||||
addOptions() {
|
||||
|
@ -224,14 +215,21 @@ export const SearchNReplace = Extension.create<SearchOptions>({
|
|||
};
|
||||
},
|
||||
|
||||
addStorage() {
|
||||
return {
|
||||
results: [],
|
||||
currentIndex: -1,
|
||||
};
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
setSearchTerm:
|
||||
(searchTerm: string) =>
|
||||
({ state, dispatch, editor }) => {
|
||||
this.options.searchTerm = searchTerm;
|
||||
this.options.results = [];
|
||||
this.options.currentIndex = 0;
|
||||
this.storage.results = [];
|
||||
this.storage.currentIndex = 0;
|
||||
(editor as Editor).eventEmitter && (editor as Editor).eventEmitter.emit(ON_SEARCH_RESULTS);
|
||||
updateView(state, dispatch);
|
||||
return false;
|
||||
|
@ -247,31 +245,36 @@ export const SearchNReplace = Extension.create<SearchOptions>({
|
|||
},
|
||||
replace:
|
||||
() =>
|
||||
({ state, dispatch }) => {
|
||||
const { replaceTerm, results, currentIndex } = this.options;
|
||||
({ state, dispatch, editor }) => {
|
||||
const { replaceTerm } = this.options;
|
||||
const { currentIndex, results } = this.storage;
|
||||
const currentResult = results[currentIndex];
|
||||
|
||||
if (currentResult) {
|
||||
replace(replaceTerm, [currentResult], { state, dispatch });
|
||||
this.options.results.splice(currentIndex, 1);
|
||||
this.storage.results.splice(currentIndex, 1);
|
||||
} else {
|
||||
replace(replaceTerm, results, { state, dispatch });
|
||||
|
||||
this.options.results.shift();
|
||||
this.storage.results.shift();
|
||||
}
|
||||
|
||||
(editor as Editor).eventEmitter && (editor as Editor).eventEmitter.emit(ON_SEARCH_RESULTS);
|
||||
|
||||
updateView(state, dispatch);
|
||||
|
||||
return false;
|
||||
},
|
||||
replaceAll:
|
||||
() =>
|
||||
({ state, tr, dispatch }) => {
|
||||
const { replaceTerm, results } = this.options;
|
||||
({ state, tr, dispatch, editor }) => {
|
||||
const { replaceTerm } = this.options;
|
||||
const { results } = this.storage;
|
||||
|
||||
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);
|
||||
|
||||
|
@ -279,11 +282,12 @@ export const SearchNReplace = Extension.create<SearchOptions>({
|
|||
},
|
||||
goToPrevSearchResult:
|
||||
() =>
|
||||
({ view, tr }) => {
|
||||
const { currentIndex, results, searchResultCurrentClass } = this.options;
|
||||
({ view, tr, editor }) => {
|
||||
const { searchResultCurrentClass } = this.options;
|
||||
const { currentIndex, results } = this.storage;
|
||||
const nextIndex = (currentIndex + results.length - 1) % results.length;
|
||||
this.options.currentIndex = nextIndex;
|
||||
this.options.onChange && this.options.onChange();
|
||||
this.storage.currentIndex = nextIndex;
|
||||
(editor as Editor).eventEmitter && (editor as Editor).eventEmitter.emit(ON_SEARCH_RESULTS);
|
||||
return gotoSearchResult({
|
||||
view,
|
||||
tr,
|
||||
|
@ -294,11 +298,13 @@ export const SearchNReplace = Extension.create<SearchOptions>({
|
|||
},
|
||||
goToNextSearchResult:
|
||||
() =>
|
||||
({ view, tr }) => {
|
||||
const { currentIndex, results, searchResultCurrentClass } = this.options;
|
||||
({ view, tr, editor }) => {
|
||||
const { searchResultCurrentClass } = this.options;
|
||||
const { currentIndex, results } = this.storage;
|
||||
const nextIndex = (currentIndex + 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({
|
||||
view,
|
||||
tr,
|
||||
|
@ -334,7 +340,10 @@ export const SearchNReplace = Extension.create<SearchOptions>({
|
|||
regex(searchTerm, disableRegex, caseSensitive),
|
||||
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);
|
||||
if (ctx.getMeta('directDecoration')) {
|
||||
const { fromPos, toPos, attrs } = ctx.getMeta('directDecoration');
|
||||
|
|
|
@ -32,8 +32,11 @@ export const Search: React.FC<{ editor: Editor }> = ({ editor }) => {
|
|||
setReplaceValue('');
|
||||
setCurrentIndex(-1);
|
||||
setResults([]);
|
||||
|
||||
editor.commands.setSearchTerm('');
|
||||
editor.commands.setReplaceTerm('');
|
||||
}
|
||||
}, [visible]);
|
||||
}, [editor, visible]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!visible) return;
|
||||
|
@ -59,8 +62,8 @@ export const Search: React.FC<{ editor: Editor }> = ({ editor }) => {
|
|||
const listener = () => {
|
||||
if (!visible) return;
|
||||
|
||||
const currentIndex = searchExtension ? searchExtension.options.currentIndex : -1;
|
||||
const results = searchExtension ? searchExtension.options.results : [];
|
||||
const currentIndex = searchExtension ? searchExtension.storage.currentIndex : -1;
|
||||
const results = searchExtension ? searchExtension.storage.results : [];
|
||||
setCurrentIndex((preIndex) => (preIndex !== currentIndex ? currentIndex : preIndex));
|
||||
setResults((prevResults) => (deepEqual(prevResults, results) ? prevResults : results));
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue