Files
spsays-training/components/text-highlighter.tsx
T
2026-05-06 00:40:24 +05:30

61 lines
2.0 KiB
TypeScript

"use client";
import { useEffect } from "react";
import { useSearchParams } from "next/navigation";
import { decodeTextFragment, findAndHighlightText } from "@/lib/text-highlight";
/**
* TextHighlighter — processes incoming `?hl=` URLs on page load.
*
* When a user visits a URL with `?hl=prefix,suffix,length`, this component:
* 1. Decodes the text fragment from the query param
* 2. Searches the page's content areas for matching text
* 3. Wraps the matched text in a <mark> with a highlight animation
* 4. Scrolls to the highlighted text
* 5. Fades the highlight out after a few seconds
*
* This is rendered once in layout.tsx alongside TextSelectionToolbar.
* It only activates when the `hl` search param is present.
*/
export function TextHighlighter() {
const searchParams = useSearchParams();
const hl = searchParams.get("hl");
useEffect(() => {
if (!hl) return;
const fragment = decodeTextFragment(hl);
if (!fragment) return;
// Wait for the page content to be fully rendered before searching.
// Use a series of attempts in case content loads asynchronously
// (e.g., client components hydrating, paginated verse loading, etc.)
let attempts = 0;
const maxAttempts = 10;
const interval = 500; // ms between attempts
function tryHighlight() {
attempts++;
const found = findAndHighlightText(fragment!);
if (!found && attempts < maxAttempts) {
setTimeout(tryHighlight, interval);
}
if (found) {
// Clean the URL without triggering a navigation — remove the hl param
// so the user can share a clean URL after viewing
const url = new URL(window.location.href);
url.searchParams.delete("hl");
const cleanUrl = url.pathname + (url.search || "") + url.hash;
window.history.replaceState({}, "", cleanUrl);
}
}
// First attempt after a short delay to let the DOM settle
setTimeout(tryHighlight, 300);
}, [hl]);
return null;
}