web: add full support for highlighting results in notes search

This commit is contained in:
Abdullah Atta
2025-05-22 11:07:24 +05:00
committed by Abdullah Atta
parent 4aab23b117
commit 23de5d2229
4 changed files with 74 additions and 20 deletions

View File

@@ -475,6 +475,7 @@ export function Editor(props: EditorProps) {
);
const setEditorSaveState = useEditorStore((store) => store.setSaveState);
useScrollToBlock(session);
useScrollToSearchResult(session);
useEffect(() => {
if (!autoSaveToast.show) {
@@ -837,6 +838,25 @@ function useScrollToBlock(session: EditorSession) {
}, [session.id, session.type, blockId]);
}
function useScrollToSearchResult(session: EditorSession) {
const index = useEditorStore(
(store) => store.getSession(session.id)?.activeSearchResultIndex
);
useEffect(() => {
if (index === undefined) return;
const scrollContainer = document.getElementById(
`editorScroll_${session.id}`
);
const elements = scrollContainer?.getElementsByTagName("nn-search-result");
setTimeout(() =>
elements?.item(index)?.scrollIntoView({ block: "center" })
);
useEditorStore.getState().updateSession(session.id, [session.type], {
activeSearchResultIndex: undefined
});
}, [session.id, session.type, index]);
}
function isFile(e: DragEvent) {
return (
e.dataTransfer &&

View File

@@ -27,7 +27,7 @@ import { ChevronDown, ChevronRight } from "../icons";
type SearchResultProps = {
item: HighlightedResult;
match?: Match[];
matchIndex?: number;
depth: number;
isExpandable: boolean;
isExpanded: boolean;
@@ -36,23 +36,43 @@ type SearchResultProps = {
};
function SearchResult(props: SearchResultProps) {
const { item, match, collapse, depth, expand, isExpandable, isExpanded } =
props;
const {
item,
matchIndex,
collapse,
depth,
expand,
isExpandable,
isExpanded
} = props;
const isOpened = useEditorStore((store) => store.isNoteOpen(item.id));
const match = matchIndex !== undefined ? item.content[matchIndex] : undefined;
return (
<ListItem
isFocused={isOpened}
isCompact={!match}
item={item}
onClick={() =>
onClick={() => {
let activeIndex = 0;
for (let i = 0; i <= (matchIndex || 0) - 1; ++i) {
activeIndex += item.content[i].length;
}
useEditorStore
.getState()
.openSession(item.id, { considerPinnedTab: true })
}
.openSession(item.id, {
rawContent: item.rawContent,
force: true,
activeSearchResultIndex: activeIndex
});
}}
onMiddleClick={() =>
useEditorStore.getState().openSession(item.id, { openInNewTab: true })
useEditorStore.getState().openSession(item.id, {
openInNewTab: true,
rawContent: item.rawContent,
force: true
})
}
title={
<Flex sx={{ alignItems: "center", gap: "small" }}>

View File

@@ -83,6 +83,10 @@ export type BaseEditorSession = {
* The id of block to scroll to after opening the session successfully.
*/
activeBlockId?: string;
/**
* The index of search result to scroll to after opening the session successfully.
*/
activeSearchResultIndex?: number;
};
export type LockedEditorSession = BaseEditorSession & {
@@ -660,10 +664,8 @@ class EditorStore extends BaseStore<EditorStore> {
activeBlockId?: string;
silent?: boolean;
openInNewTab?: boolean;
/**
* Should be true if we want to open a new tab when the active tab is pinned
*/
considerPinnedTab?: boolean;
rawContent?: string;
activeSearchResultIndex?: number;
} = {}
): Promise<void> => {
const {
@@ -787,6 +789,7 @@ class EditorStore extends BaseStore<EditorStore> {
id: sessionId,
content,
activeBlockId: options.activeBlockId,
activeSearchResultIndex: options.activeSearchResultIndex,
tabId
},
options.silent
@@ -803,10 +806,14 @@ class EditorStore extends BaseStore<EditorStore> {
type: "readonly",
note,
id: sessionId,
content,
content:
options.rawContent && content
? { data: options.rawContent, type: content.type }
: content,
color: colors[0]?.fromId,
tags,
activeBlockId: options.activeBlockId,
activeSearchResultIndex: options.activeSearchResultIndex,
tabId
},
options.silent
@@ -822,8 +829,12 @@ class EditorStore extends BaseStore<EditorStore> {
attachmentsLength,
tags,
color: colors[0]?.fromId,
content,
content:
options.rawContent && content
? { ...content, data: options.rawContent }
: content,
activeBlockId: options.activeBlockId,
activeSearchResultIndex: options.activeSearchResultIndex,
tabId
},
options.silent

View File

@@ -41,7 +41,7 @@ function Home() {
const treeRef = useRef<
VirtualizedTreeHandle<{
item: HighlightedResult;
match?: Match[];
matchIndex?: number;
}>
>(null);
const notes = useStore((store) => store.notes);
@@ -124,7 +124,7 @@ function Home() {
getChildNodes={async (parent) => {
const nodes: TreeNode<{
item: HighlightedResult;
match?: Match[];
matchIndex?: number;
}>[] = [];
if (parent.id === "root") {
for (let i = 0; i < filteredItems.length; ++i) {
@@ -140,13 +140,16 @@ function Home() {
});
}
} else {
let i = 0;
for (const match of parent.data.item.content || []) {
for (
let i = 0;
i < (parent.data.item.content.length || 0);
++i
) {
nodes.push({
data: { item: parent.data.item, match },
data: { item: parent.data.item, matchIndex: i },
depth: parent.depth + 1,
parentId: parent.id,
id: parent.id + i++,
id: parent.id + i,
hasChildren: false
});
}
@@ -159,7 +162,7 @@ function Home() {
depth={node.depth}
isExpandable={node.hasChildren}
item={node.data.item}
match={node.data.match}
matchIndex={node.data.matchIndex}
isExpanded={expanded}
collapse={collapse}
expand={expand}