fix: support multiple search results within a single summary in HypaV3 modal
This commit is contained in:
@@ -53,6 +53,10 @@
|
|||||||
interface SearchResult {
|
interface SearchResult {
|
||||||
element: HTMLElement;
|
element: HTMLElement;
|
||||||
matchType: "chatMemo" | "summary";
|
matchType: "chatMemo" | "summary";
|
||||||
|
summaryPosition?: {
|
||||||
|
start: number;
|
||||||
|
end: number;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SearchUI {
|
interface SearchUI {
|
||||||
@@ -177,66 +181,69 @@
|
|||||||
summaryUIStates.forEach((summaryUI) => {
|
summaryUIStates.forEach((summaryUI) => {
|
||||||
const textAreaText = summaryUI.originalRef.value?.toLowerCase();
|
const textAreaText = summaryUI.originalRef.value?.toLowerCase();
|
||||||
|
|
||||||
if (textAreaText.includes(normalizedQuery)) {
|
let pos = -1;
|
||||||
|
while (
|
||||||
|
(pos = textAreaText.indexOf(normalizedQuery, pos + 1)) !== -1
|
||||||
|
) {
|
||||||
results.push({
|
results.push({
|
||||||
element: summaryUI.originalRef as HTMLTextAreaElement,
|
element: summaryUI.originalRef as HTMLTextAreaElement,
|
||||||
matchType: "summary",
|
matchType: "summary",
|
||||||
|
summaryPosition: {
|
||||||
|
start: pos,
|
||||||
|
end: pos + normalizedQuery.length,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
searchUIState.results = results;
|
searchUIState.results = results;
|
||||||
searchUIState.currentIndex = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rotate search results
|
if (searchUIState.results.length === 0) return;
|
||||||
if (searchUIState.results.length > 0) {
|
|
||||||
searchUIState.currentIndex =
|
|
||||||
(searchUIState.currentIndex + 1) % searchUIState.results.length;
|
|
||||||
|
|
||||||
const currentResult = searchUIState.results[searchUIState.currentIndex];
|
// Move to next result
|
||||||
|
searchUIState.currentIndex =
|
||||||
|
(searchUIState.currentIndex + 1) % searchUIState.results.length;
|
||||||
|
|
||||||
// Scroll to element
|
const result = searchUIState.results[searchUIState.currentIndex];
|
||||||
currentResult.element.scrollIntoView({
|
|
||||||
behavior: "instant",
|
|
||||||
block: "center",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (currentResult.matchType === "chatMemo") {
|
// Scroll to element
|
||||||
// Simulate focus effect
|
result.element.scrollIntoView({
|
||||||
currentResult.element.classList.add("ring-2", "ring-zinc-500");
|
behavior: "instant",
|
||||||
|
block: "center",
|
||||||
|
});
|
||||||
|
|
||||||
// Remove focus effect after a short delay
|
if (result.matchType === "chatMemo") {
|
||||||
window.setTimeout(() => {
|
// Highlight chatMemo result
|
||||||
currentResult.element.classList.remove("ring-2", "ring-zinc-500");
|
result.element.classList.add("ring-2", "ring-zinc-500");
|
||||||
}, 1000);
|
|
||||||
} else {
|
|
||||||
const textarea = currentResult.element as HTMLTextAreaElement;
|
|
||||||
const startIndex = textarea.value
|
|
||||||
.toLowerCase()
|
|
||||||
.indexOf(normalizedQuery);
|
|
||||||
const lineHeight = parseInt(
|
|
||||||
window.getComputedStyle(textarea).lineHeight,
|
|
||||||
10
|
|
||||||
);
|
|
||||||
|
|
||||||
if (startIndex !== -1) {
|
// Remove highlight after a short delay
|
||||||
// Select query
|
window.setTimeout(() => {
|
||||||
textarea.setSelectionRange(
|
result.element.classList.remove("ring-2", "ring-zinc-500");
|
||||||
startIndex,
|
}, 1000);
|
||||||
startIndex + normalizedQuery.length
|
} else {
|
||||||
);
|
// Handle summary text selection
|
||||||
|
const textarea = result.element as HTMLTextAreaElement;
|
||||||
|
|
||||||
// Scroll to the bottom
|
// Make readonly temporarily
|
||||||
textarea.scrollTop = textarea.scrollHeight;
|
textarea.readOnly = true;
|
||||||
|
|
||||||
textarea.blur(); // Collapse selection
|
// Select query
|
||||||
textarea.focus(); // This scrolls the textarea
|
textarea.setSelectionRange(
|
||||||
|
result.summaryPosition.start,
|
||||||
|
result.summaryPosition.end
|
||||||
|
);
|
||||||
|
|
||||||
searchUIState.ref.focus(); // Restore focus to search bar
|
textarea.scrollTop = textarea.scrollHeight; // Scroll to the bottom
|
||||||
}
|
textarea.blur(); // Collapse selection
|
||||||
}
|
textarea.focus(); // This scrolls the textarea
|
||||||
|
|
||||||
|
// Highlight textarea
|
||||||
|
window.setTimeout(() => {
|
||||||
|
searchUIState.ref.focus(); // Restore focus to search bar
|
||||||
|
textarea.readOnly = false; // Remove readonly after focus moved
|
||||||
|
}, 300);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -842,7 +849,7 @@
|
|||||||
|
|
||||||
{#if searchUIState.results.length > 0}
|
{#if searchUIState.results.length > 0}
|
||||||
<span
|
<span
|
||||||
class="absolute right-3 top-1/2 -translate-y-1/2 px-1.5 sm:py-3 py-1 sm:py-2 rounded text-sm font-semibold text-zinc-100 bg-zinc-700/65"
|
class="absolute right-3 top-1/2 -translate-y-1/2 px-1.5 sm:px-3 py-1 sm:py-2 rounded text-sm font-semibold text-zinc-100 bg-zinc-700/65"
|
||||||
>
|
>
|
||||||
{searchUIState.currentIndex + 1}/{searchUIState.results
|
{searchUIState.currentIndex + 1}/{searchUIState.results
|
||||||
.length}
|
.length}
|
||||||
|
|||||||
Reference in New Issue
Block a user