Skip to content

Commit

Permalink
Added scrolling to verse reference in resource viewer and highlight i…
Browse files Browse the repository at this point in the history
…ndicator
  • Loading branch information
tjcouch-sil committed Apr 18, 2024
1 parent 2efce0c commit 67541c2
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 4 deletions.
28 changes: 28 additions & 0 deletions extensions/src/resource-viewer/src/resource-viewer.web-view.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,31 @@
body {
background-color: #eee;
}

// #region verse number highlight

// Highlight keyframes thanks to chazsolo at https://stackoverflow.com/a/55835473
@keyframes highlight {
from {
background-color: yellow;
}
}

.editor-container .highlighted {
position: relative;
}

.editor-container .highlighted::before {
content: '';
position: absolute;
width: 25px;
height: 25px;
border-radius: 50%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
opacity: 0.8;
animation: highlight 2s;
}

// #endregion
77 changes: 73 additions & 4 deletions extensions/src/resource-viewer/src/resource-viewer.web-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,51 @@ const defaultScrRef: ScriptureReference = {
verseNum: 1,
};

function scrollToScrRef(scrRef: ScriptureReference) {
// We are querying for a span, so this Element will be an HTMLElement
// eslint-disable-next-line no-type-assertion/no-type-assertion
const verseElement = document.querySelector(
`.editor-container span[data-marker="v"][data-number="${scrRef.verseNum}"]`,
) as HTMLElement | undefined;
if (verseElement) {
window.scrollTo({
top: verseElement.getBoundingClientRect().top + window.scrollY - 55,
behavior: 'smooth',
});
}
return verseElement;
}

globalThis.webViewComponent = function ResourceViewer({
useWebViewState,
}: WebViewProps): JSX.Element {
const [projectId] = useWebViewState<string>('projectId', '');
logger.debug(`Resource Viewer project ID: ${projectId}`);

// This ref becomes defined when passed to the editor.
// eslint-disable-next-line no-type-assertion/no-type-assertion, no-null/no-null
const editorRef = useRef<EditorRef>(null!);
const [scrRef, setScrRef] = useSetting('platform.verseRef', defaultScrRef);
// Using react's ref api which uses null, so we must use null
// eslint-disable-next-line no-null/no-null
const editorRef = useRef<EditorRef | null>(null);
const [scrRef, setScrRefInternal] = useSetting('platform.verseRef', defaultScrRef);

/**
* Scripture reference we set most recently. Used so we don't scroll on updates to scrRef that
* come from us
*/
const internallySetScrRefRef = useRef<ScriptureReference | undefined>(undefined);

const setScrRef = useCallback(
(newScrRef: ScriptureReference) => {
internallySetScrRefRef.current = newScrRef;
return setScrRefInternal(newScrRef);
},
[setScrRefInternal],
);

/**
* Whether we have gotten the Scripture data for the very first time. Used to scroll to the
* current scrRef on startup
*/
const hasFirstRetrievedScripture = useRef(false);

const [usx, setUsx] = useProjectData('ParatextStandard', projectId).ChapterUSX(
useMemo(() => new VerseRef(scrRef.bookNum, scrRef.chapterNum, scrRef.verseNum), [scrRef]),
Expand All @@ -47,8 +82,42 @@ globalThis.webViewComponent = function ResourceViewer({
if (usx) editorRef.current?.setUsj(usxStringToUsj(usx));
}, [usx]);

useEffect(() => {
if (usx && !hasFirstRetrievedScripture.current) {
hasFirstRetrievedScripture.current = true;
// Wait 100 ms before scrolling to make sure there is plenty of time for the editor to load
// TODO: hook into the editor and detect when it has loaded somehow
setTimeout(() => scrollToScrRef(scrRef), 100);
}
}, [usx, scrRef]);

const viewOptions = useMemo(() => getViewOptions('formatted'), []);

// Scroll the selected verse into view
useEffect(() => {
// If we made this latest scrRef change, don't scroll
if (
internallySetScrRefRef.current &&
internallySetScrRefRef.current.bookNum === scrRef.bookNum &&
internallySetScrRefRef.current.chapterNum === scrRef.chapterNum &&
internallySetScrRefRef.current.verseNum === scrRef.verseNum
) {
internallySetScrRefRef.current = undefined;
return () => {};
}

// Add a highlight to the current verse element
const highlightedVerseElement = scrollToScrRef(scrRef);
if (highlightedVerseElement) highlightedVerseElement.classList.add('highlighted');

internallySetScrRefRef.current = undefined;

return () => {
// Remove highlight from the current verse element
if (highlightedVerseElement) highlightedVerseElement.classList.remove('highlighted');
};
}, [scrRef]);

return (
<Editor
ref={editorRef}
Expand Down

0 comments on commit 67541c2

Please sign in to comment.