(...names: T[]) {
/**
* Generates a unique name via a random alphanumeric string of a fixed length.
- *
+ *
*
* Generally used to assign names to inputs that are unique, especially useful for widgets inside tabs.
- *
+ *
* @param prefix a prefix to add to the unique name.
* @returns a name with the given prefix and a random alpanumeric string appended to it.
*/
@@ -196,7 +197,7 @@ export function useUniqueName(prefix?: string) {
export function useNoteContext() {
const [ noteContext, setNoteContext ] = useState();
const [ notePath, setNotePath ] = useState();
- const [ note, setNote ] = useState();
+ const [ note, setNote ] = useState();
const [ refreshCounter, setRefreshCounter ] = useState(0);
useEffect(() => {
@@ -205,7 +206,7 @@ export function useNoteContext() {
useTriliumEvents([ "setNoteContext", "activeContextChanged", "noteSwitchedAndActivated", "noteSwitched" ], ({ noteContext }) => {
setNoteContext(noteContext);
- setNotePath(noteContext.notePath);
+ setNotePath(noteContext.notePath);
});
useTriliumEvent("frocaReloaded", () => {
setNote(noteContext?.note);
@@ -235,7 +236,7 @@ export function useNoteContext() {
/**
* Allows a React component to listen to obtain a property of a {@link FNote} while also automatically watching for changes, either via the user changing to a different note or the property being changed externally.
- *
+ *
* @param note the {@link FNote} whose property to obtain.
* @param property a property of a {@link FNote} to obtain the value from (e.g. `title`, `isProtected`).
* @param componentId optionally, constricts the refresh of the value if an update occurs externally via the component ID of a legacy widget. This can be used to avoid external data replacing fresher, user-inputted data.
@@ -287,7 +288,7 @@ export function useNoteRelation(note: FNote | undefined | null, relationName: st
/**
* Allows a React component to read or write a note's label while also reacting to changes in value.
- *
+ *
* @param note the note whose label to read/write.
* @param labelName the name of the label to read/write.
* @returns an array where the first element is the getter and the second element is the setter. The setter has a special behaviour for convenience: if the value is undefined, the label is created without a value (e.g. a tag), if the value is null then the label is removed.
@@ -352,9 +353,9 @@ export function useNoteLabelBoolean(note: FNote | undefined | null, labelName: s
export function useNoteBlob(note: FNote | null | undefined): [ FBlob | null | undefined ] {
const [ blob, setBlob ] = useState();
-
+
function refresh() {
- note?.getBlob().then(setBlob);
+ note?.getBlob().then(setBlob);
}
useEffect(refresh, [ note?.noteId ]);
@@ -388,7 +389,7 @@ export function useLegacyWidget(widgetFactory: () => T, {
if (noteContext && widget instanceof NoteContextAwareWidget) {
widget.setNoteContextEvent({ noteContext });
}
-
+
const renderedWidget = widget.render();
return [ widget, renderedWidget ];
}, []);
@@ -415,7 +416,7 @@ export function useLegacyWidget(widgetFactory: () => T, {
/**
* Attaches a {@link ResizeObserver} to the given ref and reads the bounding client rect whenever it changes.
- *
+ *
* @param ref a ref to a {@link HTMLElement} to determine the size and observe the changes in size.
* @returns the size of the element, reacting to changes.
*/
@@ -445,7 +446,7 @@ export function useElementSize(ref: RefObject) {
/**
* Obtains the inner width and height of the window, as well as reacts to changes in size.
- *
+ *
* @returns the width and height of the window.
*/
export function useWindowSize() {
@@ -453,7 +454,7 @@ export function useWindowSize() {
windowWidth: window.innerWidth,
windowHeight: window.innerHeight
});
-
+
useEffect(() => {
function onResize() {
setSize({
@@ -499,7 +500,7 @@ export function useTooltip(elRef: RefObject, config: Partial(externalRef?: RefObject, initialValue: T | nu
}, [ ref, externalRef ]);
return ref;
-}
\ No newline at end of file
+}
+
+export function useSearchHighlighlighting(ref: RefObject, highlightedTokens: string[] | null | undefined) {
+ const mark = useRef();
+ const highlightRegex = useMemo(() => {
+ if (!highlightedTokens?.length) return null;
+ const regex = highlightedTokens.map((token) => escapeRegExp(token)).join("|");
+ return new RegExp(regex, "gi")
+ }, [ highlightedTokens ]);
+
+ useEffect(() => {
+ if (!ref.current || !highlightRegex) return;
+
+ if (!mark.current) {
+ mark.current = new Mark(ref.current);
+ }
+
+ mark.current.markRegExp(highlightRegex, {
+ element: "span",
+ className: "ck-find-result"
+ });
+
+ return () => mark.current?.unmark();
+ });
+}
diff --git a/apps/client/src/widgets/search_result.tsx b/apps/client/src/widgets/search_result.tsx
index adfb1b0a6..abe9d4174 100644
--- a/apps/client/src/widgets/search_result.tsx
+++ b/apps/client/src/widgets/search_result.tsx
@@ -1,11 +1,12 @@
-import { useEffect, useRef, useState } from "preact/hooks";
+import { useEffect, useState } from "preact/hooks";
import { t } from "../services/i18n";
import Alert from "./react/Alert";
-import { useNoteContext, useNoteProperty, useTriliumEvent } from "./react/hooks";
+import { useNoteContext, useTriliumEvent } from "./react/hooks";
import "./search_result.css";
+import NoteList from "./collections/NoteList";
// import NoteListRenderer from "../services/note_list_renderer";
-enum SearchResultState {
+enum SearchResultState {
NO_RESULTS,
NOT_EXECUTED,
GOT_RESULTS
@@ -14,27 +15,18 @@ enum SearchResultState {
export default function SearchResult() {
const { note, ntxId } = useNoteContext();
const [ state, setState ] = useState();
- const searchContainerRef = useRef(null);
+ const [ highlightedTokens, setHighlightedTokens ] = useState();
function refresh() {
- searchContainerRef.current?.replaceChildren();
-
if (note?.type !== "search") {
setState(undefined);
} else if (!note?.searchResultsLoaded) {
setState(SearchResultState.NOT_EXECUTED);
} else if (note.getChildNoteIds().length === 0) {
setState(SearchResultState.NO_RESULTS);
- } else if (searchContainerRef.current) {
+ } else {
setState(SearchResultState.GOT_RESULTS);
-
- // TODO: Fix me.
- // const noteListRenderer = new NoteListRenderer({
- // $parent: $(searchContainerRef.current),
- // parentNote: note,
- // showNotePath: true
- // });
- // noteListRenderer.renderList();
+ setHighlightedTokens(note.highlightedTokens);
}
}
@@ -60,7 +52,9 @@ export default function SearchResult() {
{t("search_result.no_notes_found")}
)}
-
+ {state === SearchResultState.GOT_RESULTS && (
+
+ )}
);
-}
\ No newline at end of file
+}