Skip to content

Commit 2200592

Browse files
committed
feat: search loader
1 parent 658845c commit 2200592

1 file changed

Lines changed: 68 additions & 64 deletions

File tree

apps/web/src/pages/SpecExplorer.tsx

Lines changed: 68 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -167,83 +167,87 @@ export function SpecExplorer() {
167167
value={search}
168168
onChange={(e) => setSearch(e.target.value)}
169169
onKeyDown={handleKeyDown}
170-
placeholder="Ask about the ECMA-376 spec..."
170+
placeholder="e.g. paragraph spacing, table borders, how to style text runs..."
171171
rows={2}
172172
className="w-full resize-none rounded-lg border border-[var(--color-border)] bg-transparent px-4 py-3 text-sm leading-relaxed outline-none transition placeholder:text-[var(--color-text-muted)] focus:border-[var(--color-text-primary)] focus:shadow-[0_0_0_3px_rgba(0,0,0,0.05)]"
173173
autoFocus
174174
/>
175-
<div className="mt-2 text-[11px] text-[var(--color-text-muted)]">
176-
Press{" "}
177-
<kbd className="rounded border border-[var(--color-border)] bg-[var(--color-bg-secondary)] px-1">
178-
Enter
179-
</kbd>{" "}
180-
to search
175+
<div className="mt-2 flex items-center justify-between text-[11px] text-[var(--color-text-muted)]">
176+
<span>Use natural language or element names</span>
177+
<span>
178+
<kbd className="rounded border border-[var(--color-border)] bg-[var(--color-bg-secondary)] px-1.5 py-0.5">
179+
180+
</kbd>
181+
</span>
181182
</div>
182183
</div>
183184

184185
{/* Results */}
185-
<div ref={resultsRef} className="min-h-0 flex-1 overflow-y-auto">
186-
{/* Results count */}
187-
{results.length > 0 && (
188-
<div className="px-5 pb-2 text-xs text-[var(--color-text-muted)]">
189-
{results.length} results
190-
</div>
191-
)}
186+
<div className="relative min-h-0 flex-1">
187+
<div ref={resultsRef} className="h-full overflow-y-auto">
188+
{/* Results count */}
189+
{results.length > 0 && (
190+
<div className="px-5 pb-2 text-xs text-[var(--color-text-muted)]">
191+
{results.length} results
192+
</div>
193+
)}
192194

193-
{/* Loading state */}
194-
{isLoading && (
195-
<div className="flex items-center justify-center py-12">
196-
<Loader2 size={24} className="animate-spin text-[var(--color-text-muted)]" />
197-
</div>
198-
)}
195+
{/* Empty state */}
196+
{!isLoading && !submittedSearch && (
197+
<div className="px-5 py-12 text-center text-sm text-[var(--color-text-muted)]">
198+
Search the ECMA-376 specification
199+
</div>
200+
)}
199201

200-
{/* Empty state */}
201-
{!isLoading && !submittedSearch && (
202-
<div className="px-5 py-12 text-center text-sm text-[var(--color-text-muted)]">
203-
Search the ECMA-376 specification
204-
</div>
205-
)}
202+
{/* No results */}
203+
{!isLoading && submittedSearch && results.length === 0 && (
204+
<div className="px-5 py-12 text-center text-sm text-[var(--color-text-muted)]">
205+
No results found
206+
</div>
207+
)}
206208

207-
{/* No results */}
208-
{!isLoading && submittedSearch && results.length === 0 && (
209-
<div className="px-5 py-12 text-center text-sm text-[var(--color-text-muted)]">
210-
No results found
209+
{/* Results list */}
210+
{results.map((result, idx) => (
211+
<button
212+
key={result.id}
213+
type="button"
214+
data-selected={idx === selectedIndex}
215+
onClick={() => handleSelectResult(result, idx)}
216+
onMouseEnter={() => setSelectedIndex(idx)}
217+
className={clsx(
218+
"w-full cursor-pointer border-b border-[var(--color-bg-tertiary)] px-5 py-3 text-left transition",
219+
idx === selectedIndex
220+
? "bg-[var(--color-bg-tertiary)]"
221+
: "hover:bg-[var(--color-bg-secondary)]",
222+
)}
223+
>
224+
<div className="flex items-baseline gap-2">
225+
<span className="shrink-0 font-mono text-[11px] font-medium text-[var(--color-accent)]">
226+
§{result.sectionId}
227+
</span>
228+
<span className="truncate text-sm font-medium text-[var(--color-text-primary)]">
229+
{result.title}
230+
</span>
231+
</div>
232+
{result.description && (
233+
<div className="mt-1 line-clamp-1 text-xs text-[var(--color-text-muted)]">
234+
{result.description}
235+
</div>
236+
)}
237+
<div className="mt-1.5 text-[10px] text-[var(--color-text-muted)]">
238+
Part {result.partNumber} · Page {result.pageNumber || "—"}
239+
</div>
240+
</button>
241+
))}
242+
</div>
243+
244+
{/* Loading overlay */}
245+
{isLoading && (
246+
<div className="absolute inset-0 flex flex-col items-center justify-center gap-3 bg-white/85">
247+
<Loader2 size={28} className="animate-spin text-[var(--color-accent)]" />
248+
<span className="text-sm text-[var(--color-text-secondary)]">Searching...</span>
211249
</div>
212250
)}
213-
214-
{/* Results list */}
215-
{results.map((result, idx) => (
216-
<button
217-
key={result.id}
218-
type="button"
219-
data-selected={idx === selectedIndex}
220-
onClick={() => handleSelectResult(result, idx)}
221-
onMouseEnter={() => setSelectedIndex(idx)}
222-
className={clsx(
223-
"w-full cursor-pointer border-b border-[var(--color-bg-tertiary)] px-5 py-3 text-left transition",
224-
idx === selectedIndex
225-
? "bg-[var(--color-bg-tertiary)]"
226-
: "hover:bg-[var(--color-bg-secondary)]",
227-
)}
228-
>
229-
<div className="flex items-baseline gap-2">
230-
<span className="shrink-0 font-mono text-[11px] font-medium text-[var(--color-accent)]">
231-
§{result.sectionId}
232-
</span>
233-
<span className="truncate text-sm font-medium text-[var(--color-text-primary)]">
234-
{result.title}
235-
</span>
236-
</div>
237-
{result.description && (
238-
<div className="mt-1 line-clamp-1 text-xs text-[var(--color-text-muted)]">
239-
{result.description}
240-
</div>
241-
)}
242-
<div className="mt-1.5 text-[10px] text-[var(--color-text-muted)]">
243-
Part {result.partNumber} · Page {result.pageNumber || "—"}
244-
</div>
245-
</button>
246-
))}
247251
</div>
248252
</div>
249253

0 commit comments

Comments
 (0)