Skip to content

Commit 20f7a29

Browse files
authored
Merge pull request #96 from vilpessoa/claude/great-sagan-N3baa
feat: 4 melhorias no componente SearchBar
2 parents ced0cdb + a8919b2 commit 20f7a29

1 file changed

Lines changed: 59 additions & 11 deletions

File tree

src/components/SearchBar.tsx

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useEffect, useRef, useState, type RefObject } from 'react';
2+
import { EditorView } from '@codemirror/view';
23
import { motion, AnimatePresence } from 'framer-motion';
34
import { Search, X, ChevronUp, ChevronDown, ChevronRight, CaseSensitive, Regex } from 'lucide-react';
45
import {
@@ -36,13 +37,32 @@ function countMatches(view: ReturnType<DaxEditorHandle['getView']>, query: Searc
3637
}
3738
}
3839

40+
function getCurrentMatchIndex(view: ReturnType<DaxEditorHandle['getView']>, query: SearchQuery): number {
41+
if (!view || !query.search) return 0;
42+
try {
43+
const cursorPos = view.state.selection.main.from;
44+
const cursor = query.getCursor(view.state.doc);
45+
let index = 0;
46+
let result = cursor.next();
47+
while (!result.done) {
48+
if (result.value.from >= cursorPos) return index;
49+
index++;
50+
result = cursor.next();
51+
}
52+
return 0;
53+
} catch {
54+
return 0;
55+
}
56+
}
57+
3958
export function SearchBar({ editorRef, onClose }: SearchBarProps) {
4059
const [searchText, setSearchText] = useState('');
4160
const [replaceText, setReplaceText] = useState('');
4261
const [caseSensitive, setCaseSensitive] = useState(false);
4362
const [useRegex, setUseRegex] = useState(false);
4463
const [showReplace, setShowReplace] = useState(false);
4564
const [matchCount, setMatchCount] = useState(0);
65+
const [currentMatchIndex, setCurrentMatchIndex] = useState(0);
4666
const [regexError, setRegexError] = useState(false);
4767

4868
const searchInputRef = useRef<HTMLInputElement>(null);
@@ -78,13 +98,37 @@ export function SearchBar({ editorRef, onClose }: SearchBarProps) {
7898
const view = editorRef.current?.getView();
7999
if (!view) return;
80100
const q = buildQuery(search);
81-
if (!q) { setMatchCount(0); return; }
101+
if (!q) { setMatchCount(0); setCurrentMatchIndex(0); return; }
82102
view.dispatch({ effects: setSearchQuery.of(q) });
83-
setMatchCount(countMatches(view, q));
103+
const count = countMatches(view, q);
104+
setMatchCount(count);
105+
106+
if (count > 0) {
107+
const cursor = q.getCursor(view.state.doc);
108+
const result = cursor.next();
109+
if (!result.done) {
110+
const { from, to } = result.value;
111+
view.dispatch({
112+
selection: { anchor: from, head: to },
113+
effects: EditorView.scrollIntoView(from)
114+
});
115+
setCurrentMatchIndex(0);
116+
}
117+
} else {
118+
setCurrentMatchIndex(0);
119+
}
84120
}
85121

86122
useEffect(() => {
87-
applyQuery(searchText);
123+
const timer = setTimeout(() => {
124+
if (searchText) {
125+
applyQuery(searchText);
126+
} else {
127+
setMatchCount(0);
128+
setCurrentMatchIndex(0);
129+
}
130+
}, 150);
131+
return () => clearTimeout(timer);
88132
// eslint-disable-next-line react-hooks/exhaustive-deps
89133
}, [searchText, caseSensitive, useRegex]);
90134

@@ -93,13 +137,17 @@ export function SearchBar({ editorRef, onClose }: SearchBarProps) {
93137
if (!view || !searchText) return;
94138
applyQuery(searchText);
95139
findNext(view);
140+
const q = buildQuery(searchText);
141+
if (q) setCurrentMatchIndex(getCurrentMatchIndex(view, q));
96142
}
97143

98144
function handleFindPrev() {
99145
const view = editorRef.current?.getView();
100146
if (!view || !searchText) return;
101147
applyQuery(searchText);
102148
findPrevious(view);
149+
const q = buildQuery(searchText);
150+
if (q) setCurrentMatchIndex(getCurrentMatchIndex(view, q));
103151
}
104152

105153
function handleReplaceAll() {
@@ -174,14 +222,14 @@ export function SearchBar({ editorRef, onClose }: SearchBarProps) {
174222
type="button"
175223
onClick={() => setShowReplace((v) => !v)}
176224
className={cn(
177-
'inline-flex h-6 w-6 flex-shrink-0 items-center justify-center rounded border transition-all',
225+
'group inline-flex h-6 w-6 flex-shrink-0 items-center justify-center rounded border transition-all',
178226
showReplace
179-
? 'border-primary/40 bg-primary/15 text-primary'
180-
: 'border-border/60 bg-background/60 text-muted-foreground hover:border-border hover:bg-accent hover:text-foreground',
227+
? 'border-primary/60 bg-primary/25 text-primary drop-shadow-sm'
228+
: 'border-border/50 bg-input/40 text-foreground/60 drop-shadow-sm hover:bg-surface-elevated/50 hover:text-primary',
181229
)}
182230
>
183231
<ChevronRight
184-
className={cn('h-3.5 w-3.5 transition-transform duration-150', showReplace && 'rotate-90')}
232+
className={cn('h-3.5 w-3.5 transition-transform duration-150 group-hover:scale-110', showReplace && 'rotate-90')}
185233
/>
186234
</button>
187235
</TooltipTrigger>
@@ -192,7 +240,7 @@ export function SearchBar({ editorRef, onClose }: SearchBarProps) {
192240

193241
{/* Search input */}
194242
<div className={cn(
195-
'relative flex h-7 flex-1 items-center rounded border bg-background/60 px-2',
243+
'relative flex h-7 flex-1 items-center rounded border bg-surface-elevated/40 px-2',
196244
regexError ? 'border-destructive' : 'border-border/60 focus-within:border-primary/60',
197245
)}>
198246
<Search className="mr-1.5 h-3 w-3 flex-shrink-0 text-muted-foreground" />
@@ -207,10 +255,10 @@ export function SearchBar({ editorRef, onClose }: SearchBarProps) {
207255
/>
208256
{searchText && (
209257
<span className={cn(
210-
'ml-1 flex-shrink-0 text-[10px] tabular-nums',
258+
'ml-1 flex-shrink-0 whitespace-nowrap text-[10px] tabular-nums',
211259
matchCount > 0 ? 'text-muted-foreground' : 'text-destructive/70',
212260
)}>
213-
{matchCount > 0 ? `${matchCount}` : 'Nenhum resultado'}
261+
{matchCount > 0 ? `${currentMatchIndex + 1}/${matchCount}` : 'Nenhum resultado'}
214262
</span>
215263
)}
216264
</div>
@@ -324,7 +372,7 @@ export function SearchBar({ editorRef, onClose }: SearchBarProps) {
324372
{/* Spacer to align with search input */}
325373
<span className="h-6 w-6 flex-shrink-0" />
326374

327-
<div className="flex h-7 flex-1 items-center rounded border border-border/60 bg-background/60 px-2 focus-within:border-primary/60">
375+
<div className="flex h-7 flex-1 items-center rounded border border-border/60 bg-surface-elevated/40 px-2 focus-within:border-primary/60">
328376
<input
329377
ref={replaceInputRef}
330378
type="text"

0 commit comments

Comments
 (0)