Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .claude/settings.local.json

This file was deleted.

3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,6 @@ vite.config.*.timestamp*
vitest.config.*.timestamp*

.env

# claude - files that are supposed to be local-only and not shared with the team
.claude/settings.local.json
7 changes: 6 additions & 1 deletion demos/platform/src/app/app.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* eslint-disable no-console */
import { BookChapterControl } from "platform-bible-react";
import AnnotationTypeSelect from "./AnnotationTypeSelect";
import NodeOptionsDropDown, {
CUSTOM_NODES_MODE,
Expand Down Expand Up @@ -486,7 +487,7 @@ export default function App() {
)}
</>
)}
{hasExternalUI && (
{hasExternalUI ? (
<PlatformToolbar
ref={toolbarEndRef}
editorRef={marginalRef}
Expand All @@ -497,6 +498,10 @@ export default function App() {
canRedo={canRedo}
blockMarker={blockMarker}
/>
) : (
<div className="tw-items-center tw-text-primary">
<BookChapterControl scrRef={scrRef} handleSubmit={setScrRef} />
</div>
)}
<Marginal
ref={marginalRef}
Expand Down
75 changes: 75 additions & 0 deletions libs/shared-react/src/nodes/usj/node-react-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { UsjNodeOptions } from "./usj-node-options.model";
import { $createTextNode, $getNodeByKey, $getRoot, NodeKey } from "lexical";
import {
$createBookNode,
$createCharNode,
$createImmutableChapterNode,
$createParaNode,
$createTypedMarkNode,
Expand Down Expand Up @@ -276,6 +277,80 @@ describe("$findThisVerse()", () => {
});
});

it("should find the last verse in node when the text is in a char node", () => {
let t2Key: string;
const { editor } = createBasicTestEnvironment([ParaNode, ImmutableVerseNode, CharNode]);
/*
* root
* p1
* v1 t1 v2 c1
* t2
* ^^
*/
editor.update(
() => {
const root = $getRoot();
const p1 = $createParaNode();
const v1 = $createImmutableVerseNode("1");
const v2 = $createImmutableVerseNode("2");
const t1 = $createTextNode("text1");
const c1 = $createCharNode("add");
const t2 = $createTextNode("text2");
root.append(p1);
p1.append(v1, t1, v2, c1);
c1.append(t2);
t2Key = t2.getKey();
},
{ discrete: true },
);

editor.getEditorState().read(() => {
const t2 = $getNodeByKey(t2Key);
const verseNode = $findThisVerse(t2);

expect(verseNode).toBeDefined();
expect(verseNode?.getNumber()).toEqual("2");
});
});

it("should find the last verse in a previous parent node when the text is in a char node", () => {
let t2Key: string;
const { editor } = createBasicTestEnvironment([ParaNode, ImmutableVerseNode, CharNode]);
/*
* root
* p1 p2
* v1 t1 v2 c1
* t2
* ^^
*/
editor.update(
() => {
const root = $getRoot();
const p1 = $createParaNode();
const p2 = $createParaNode();
const v1 = $createImmutableVerseNode("1");
const v2 = $createImmutableVerseNode("2");
const t1 = $createTextNode("text1");
const c1 = $createCharNode("add");
const t2 = $createTextNode("text2");
root.append(p1, p2);
p1.append(v1, t1, v2);
p2.append(c1);
c1.append(t2);
t2Key = t2.getKey();
},
{ discrete: true },
);

editor.getEditorState().read(() => {
const t2 = $getNodeByKey(t2Key);
const verseNode = $findThisVerse(t2);

expect(verseNode).toBeDefined();
expect(verseNode?.getNumber()).toEqual("2");
});
});

it("should find the verse in a previous parent node", () => {
let t3Key: string;
const { editor } = createBasicTestEnvironment([ParaNode, ImmutableVerseNode]);
Expand Down
55 changes: 30 additions & 25 deletions libs/shared-react/src/nodes/usj/node-react.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ import {
$isNoteNode,
$isParaNode,
$isSomeChapterNode,
$isTypedMarkNode,
$isVerseNode,
$moveSelectionToEnd,
closingMarkerText,
Expand Down Expand Up @@ -433,6 +432,22 @@ export function $findLastVerse(nodes: LexicalNode[]) {
return verseNodes[verseNodes.length - 1];
}

/**
* Finds the nearest previous node by checking the node's previous sibling, then walking up
* through ancestors and checking their previous siblings. Stops at root.
* @param node - Node to start from.
* @returns the nearest previous node, or `null` if none exists.
*/
function $findNearestPreviousNode(node: LexicalNode): LexicalNode | null {
let current: LexicalNode | null | undefined = node;
while (current && current.getParent() !== null) {
const prev = current.getPreviousSibling();
if (prev) return prev;
current = current.getParent();
}
return null;
}

/**
* Find the verse that this node is in.
* @param node - Node to find the verse it's in.
Expand All @@ -444,32 +459,22 @@ export function $findThisVerse(node: LexicalNode | null | undefined) {
// is this node a verse
if ($isSomeVerseNode(node)) return node;

// is one of the previous sibling nodes a verse
const isWrappedInMark = $isTypedMarkNode(node.getParent());
let previousSibling = isWrappedInMark
? node.getParent()?.getPreviousSibling()
: node.getPreviousSibling();
while (
previousSibling &&
!$isSomeVerseNode(previousSibling) &&
!$isSomeChapterNode(previousSibling)
) {
previousSibling = previousSibling.getPreviousSibling();
}
if (previousSibling && $isSomeVerseNode(previousSibling)) return previousSibling;

// is the verse in a previous parent sibling
let previousParentSibling = node.getTopLevelElement()?.getPreviousSibling();
let verseNode = $findLastVerseInNode(previousParentSibling);
let nextVerseNode = verseNode;
while (previousParentSibling && !verseNode && !$isSomeChapterNode(previousParentSibling)) {
verseNode = nextVerseNode;
previousParentSibling = previousParentSibling.getPreviousSibling();
nextVerseNode = $findLastVerseInNode(previousParentSibling);
let previousSiblingOrParent = $findNearestPreviousNode(node);
while (previousSiblingOrParent) {
// If this node is a chapter node, stop searching as we've reached the start of this chapter
if ($isSomeChapterNode(previousSiblingOrParent)) return;

// If this node is a verse node, return it
if ($isSomeVerseNode(previousSiblingOrParent)) return previousSiblingOrParent;

// If this node contains a verse node, return that
const verseNode = $findLastVerseInNode(previousSiblingOrParent);
if (verseNode) return verseNode;

previousSiblingOrParent = $findNearestPreviousNode(previousSiblingOrParent);
}
if (!verseNode && $isSomeChapterNode(previousParentSibling)) return;

return verseNode;
return undefined;
}

/**
Expand Down
Loading