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' {
|
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');
|
||||||
|
|
|
@ -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));
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue