Skip to content

Commit

Permalink
versewise navigation with arrow keys. (#300)
Browse files Browse the repository at this point in the history
* versewise navigation with arrow keys.

* highlight text, navigate on same verse multi click

* fixed scroll lock issue

* removed highlight text
  • Loading branch information
samueljd authored Jan 19, 2024
1 parent 8bea62f commit 327a013
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 55 deletions.
6 changes: 3 additions & 3 deletions renderer/src/components/EditorPage/TextEditor/Editor.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, {
useContext, useEffect, useLayoutEffect, useRef,
useContext, useEffect, useLayoutEffect, useRef,
} from 'react';
import { HtmlPerfEditor } from '@xelah/type-perf-html';

Expand Down Expand Up @@ -123,7 +123,7 @@ export default function Editor(props) {
function onReferenceSelected({ chapter, verse }) {
chapter && setChapterNumber(chapter);
verse && setVerseNumber(verse);
scrollReference(chapter);
!scrollLock && scrollReference(chapter, verse);
}

const observer = new IntersectionObserver((entries) => onIntersection({
Expand All @@ -147,7 +147,7 @@ export default function Editor(props) {
addSequenceId,
components: {
block: (__props) => RecursiveBlock({
htmlPerf, onHtmlPerf: saveHtmlPerf, sequenceIds, addSequenceId, onReferenceSelected, setCaretPosition, setSelectedText, ...__props,
htmlPerf, onHtmlPerf: saveHtmlPerf, sequenceIds, addSequenceId, onReferenceSelected, setCaretPosition, setSelectedText, scrollLock, ...__props,
}),
},
options: {
Expand Down
59 changes: 33 additions & 26 deletions renderer/src/components/EditorPage/TextEditor/RecursiveBlock.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import React, { useEffect, useState } from 'react';
import { HtmlPerfEditor } from '@xelah/type-perf-html';
import { getCurrentCursorPosition, pasteTextAtCursorPosition } from '@/util/cursorUtils';
import { getCurrentVerse, getCurrentChapter } from '@/components/EditorPage/TextEditor/utils/getReferences';
import {
getCurrentVerse, getCurrentChapter,
} from '@/components/EditorPage/TextEditor/utils/getReferences';
import { on } from 'ws';

const getTarget = ({ content }) => {
Expand Down Expand Up @@ -31,6 +33,7 @@ export default function RecursiveBlock({
onReferenceSelected,
setCaretPosition,
setSelectedText,
scrollLock,
...props
}) {
const [currentVerse, setCurrentVerse] = useState(null);
Expand All @@ -40,27 +43,6 @@ export default function RecursiveBlock({
setCaretPosition(cursorPosition);
};

const checkReturnKeyPress = (event) => {
const activeTextArea = document.activeElement;
if (event.key === 'Enter') {
if (activeTextArea.children.length > 1) {
const lineBreak = activeTextArea.children[1]?.outerHTML;
activeTextArea.children[1].outerHTML = lineBreak.replace(/<br\s*\/?>/gi, '&nbsp');
}
}
// BACKSPACE DISABLE
if (event.keyCode === 8) {
const range = document.getSelection().getRangeAt(0);
const selectedNode = range.startContainer;
const prevNode = selectedNode.previousSibling;
if (prevNode && prevNode.dataset.attsNumber !== currentVerse) {
event.preventDefault();
}
prevNode ? setCurrentVerse(prevNode.dataset.attsNumber) : {};
}
updateCursorPosition();
};

function handleSelection() {
let selectedText = '';
if (window.getSelection) {
Expand All @@ -72,19 +54,44 @@ export default function RecursiveBlock({
setSelectedText(selectedText);
}
}

const checkCurrentVerse = () => {
if (document.getSelection().rangeCount >= 1 && onReferenceSelected) {
const range = document.getSelection().getRangeAt(0);
const selectedNode = range.startContainer;
const verse = getCurrentVerse(selectedNode);
const { verse } = getCurrentVerse(selectedNode);
const chapter = getCurrentChapter(selectedNode);
onReferenceSelected({ bookId, chapter, verse });
// !scrollLock && hightlightRefVerse(chapter, verse);
}
updateCursorPosition();
handleSelection();
};

const keyStrokeHandler = (event) => {
const activeTextArea = document.activeElement;
// Replace line break with space
if (event.key === 'Enter') {
if (activeTextArea.children.length > 1) {
const lineBreak = activeTextArea.children[1]?.outerHTML;
activeTextArea.children[1].outerHTML = lineBreak.replace(/<br\s*\/?>/gi, '&nbsp');
}
}
// Disable backspace if the previous node is not the same verse
if (event.keyCode === 8) {
const range = document.getSelection().getRangeAt(0);
const selectedNode = range.startContainer;
const prevNode = selectedNode.previousSibling;
if (prevNode && prevNode.dataset.attsNumber !== currentVerse) {
event.preventDefault();
}
prevNode ? setCurrentVerse(prevNode.dataset.attsNumber) : {};
}
if ([37, 38, 39, 40].includes(event.keyCode)) {
checkCurrentVerse();
updateCursorPosition();
}
};

function onPasteHandler(event) {
const cursorPosition = getCurrentCursorPosition('editor');
const paste = (event.clipboardData || window.clipboardData).getData('text');
Expand All @@ -101,11 +108,11 @@ export default function RecursiveBlock({
<div
className="editor-paragraph"
contentEditable={contentEditable}
onKeyDown={checkReturnKeyPress}
onKeyDown={keyStrokeHandler}
onMouseUp={checkCurrentVerse}
onMouseDown={updateCursorPosition}
{...props}
onPaste={(event) => { event.preventDefault(); onPasteHandler(event); }}
{...props}
/>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
export const scrollReference = (chapterNumber) => {
const refEditors = document.getElementsByClassName('ref-editor');
refEditors.length > 0 && Array.prototype.filter.call(refEditors, (refEditor) => {
const editorInView = refEditor.querySelector(`#ch-${chapterNumber}`);
if (editorInView) {
editorInView.scrollIntoView();
editorInView.classList.add('scroll-mt-10');
}
});
};
export const scrollReference = (() => {
let prevCV;
return (c, v) => {
const refEditors = document.getElementsByClassName('ref-editor');
refEditors.length > 0 && Array.prototype.filter.call(refEditors, (refEditor) => {
if (!prevCV || prevCV.c !== c) {
const chapterInView = refEditor.querySelector(`#ch-${c}`);
if (chapterInView) {
chapterInView.scrollIntoView();
chapterInView.classList.add('scroll-mt-10');
}
} else {
const verseInView = refEditor.querySelector(`#ch${c}v${v}`);
if (verseInView) {
verseInView.scrollIntoView();
}
}
});
prevCV = { c, v };
};
})();

export const onIntersection = ({
scroll, entries, setChapterNumber, scrollLock, setVerseNumber,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,49 @@
export const getCurrentVerse = (currentNode) => {
let currentVerse;
let prev = currentNode.previousElementSibling;
while (prev) {
if (prev.dataset.type === 'mark' && prev.dataset.subtype === 'verses') {
currentVerse = prev.dataset.attsNumber;
let verse;
let previousElement = currentNode?.previousElementSibling;
const verseText = currentNode?.nextSibling;
while (previousElement) {
if (previousElement.dataset.type === 'mark' && previousElement.dataset.subtype === 'verses') {
verse = previousElement.dataset.attsNumber;
break;
}
// Get the previous sibling
prev = prev.previousElementSibling;
previousElement = previousElement?.previousElementSibling;
}
return currentVerse;
return { verse, verseText };
};

export const removeHighlightFromRefVerse = ({ c, v }) => {
const refEditors = document.getElementsByClassName('ref-editor');
refEditors.length > 0 && Array.prototype.filter.call(refEditors, (refEditor) => {
const prevHighlight = refEditor.querySelector(`#ch${c}v${v}`)?.nextElementSibling;
const hightlightText = prevHighlight && prevHighlight.innerHTML;
prevHighlight && prevHighlight.replaceWith(hightlightText);
});
};

export const hightlightRefVerse = (() => {
let prevCV;
return (c, v) => {
const refEditors = document.getElementsByClassName('ref-editor');
refEditors.length > 0 && Array.prototype.filter.call(refEditors, (refEditor) => {
if (!(prevCV && prevCV.c !== c)) {
const verseInView = refEditor.querySelector(`#ch${c}v${v}`);
const { verseText } = getCurrentVerse(verseInView);
// highlight verse
const range = document.createRange();
range.setStart(verseText, 0);
range.setEnd(verseText, verseText.textContent.length);
const newSpan = document.createElement('span');
newSpan.classList.add('bg-primary-50');
range.surroundContents(newSpan);
// remove highlight from previous verse
prevCV && removeHighlightFromRefVerse({ ...prevCV });
}
});
prevCV = { c, v };
};
})();

export const getCurrentChapter = (currentNode) => {
let currentChapter;
const closestParaDiv = currentNode.parentNode.parentNode;
Expand All @@ -20,13 +52,13 @@ export const getCurrentChapter = (currentNode) => {
return currentChapter;
}

let prevParaDiv = closestParaDiv.previousElementSibling;
while (prevParaDiv) {
if (prevParaDiv.firstElementChild?.firstElementChild?.classList.contains('chapter')) {
currentChapter = prevParaDiv.firstElementChild.firstElementChild.dataset.attsNumber;
break;
}
prevParaDiv = prevParaDiv.previousElementSibling;
let prevParaDiv = closestParaDiv.previousElementSibling;
while (prevParaDiv) {
if (prevParaDiv.firstElementChild?.firstElementChild?.classList.contains('chapter')) {
currentChapter = prevParaDiv.firstElementChild.firstElementChild.dataset.attsNumber;
break;
}
return currentChapter;
prevParaDiv = prevParaDiv.previousElementSibling;
}
return currentChapter;
};
4 changes: 4 additions & 0 deletions styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ p.paragraph:has(.chapter) {
@apply text-primary text-sm;
}

.ref-editor ::selection {
@apply bg-primary-50;
}

/* .perf .verse:after {
visibility: visible;
position: absolute;
Expand Down

0 comments on commit 327a013

Please sign in to comment.