Pure function that reads commentId + annotationId from the page URL, enters transcribe mode if needed, activates the block's annotation, scrolls the target comment into view, focuses it for screen readers, fires the existing annotation flash, and strips the params via the injected callback. All side effects go through callbacks so the helper is unit-testable without mounting the page or a DOM (only scrollIntoView/focus are called on the injected element). Eight tests cover both absent params, happy path, transcribe-mode activation, missing DOM target, reduced motion, flash trigger, and URL strip. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
41 lines
1.1 KiB
TypeScript
41 lines
1.1 KiB
TypeScript
export type DeepLinkScrollOptions = {
|
|
transcribeMode: boolean;
|
|
setTranscribeMode: (value: boolean) => void;
|
|
loadBlocks: () => Promise<void>;
|
|
setActiveAnnotationId: (id: string) => void;
|
|
flashAnnotation: (annotationId: string) => void;
|
|
prefersReducedMotion: boolean;
|
|
afterTick: () => Promise<void>;
|
|
getElement: (id: string) => HTMLElement | null;
|
|
onStripUrl: () => void;
|
|
};
|
|
|
|
export async function scrollToCommentFromQuery(
|
|
url: URL,
|
|
opts: DeepLinkScrollOptions
|
|
): Promise<void> {
|
|
const commentId = url.searchParams.get('commentId');
|
|
if (!commentId) return;
|
|
|
|
const annotationId = url.searchParams.get('annotationId');
|
|
if (!annotationId) return;
|
|
|
|
if (!opts.transcribeMode) {
|
|
opts.setTranscribeMode(true);
|
|
await opts.loadBlocks();
|
|
}
|
|
|
|
opts.setActiveAnnotationId(annotationId);
|
|
await opts.afterTick();
|
|
|
|
const el = opts.getElement(`comment-${commentId}`);
|
|
if (el) {
|
|
const behavior: ScrollBehavior = opts.prefersReducedMotion ? 'instant' : 'smooth';
|
|
el.scrollIntoView({ behavior, block: 'center' });
|
|
el.focus({ preventScroll: true });
|
|
opts.flashAnnotation(annotationId);
|
|
}
|
|
|
|
opts.onStripUrl();
|
|
}
|