import clsx from "clsx"; import { HTMLAttributes } from "preact"; import { useEffect, useRef, useState } from "preact/hooks"; import link, { calculateHash, ViewScope } from "../../services/link"; import { useImperativeSearchHighlighlighting, useNote, useNoteColorClass, useNoteIcon, useNoteLabel, useNoteLabelBoolean, useNoteProperty, useTriliumEvent } from "./hooks"; import Icon from "./Icon"; interface NoteLinkOpts { className?: string; containerClassName?: string; notePath: string | string[]; showNotePath?: boolean; showNoteIcon?: boolean; style?: Record; noPreview?: boolean; noTnLink?: boolean; highlightedTokens?: string[] | null | undefined; // Override the text of the link, otherwise the note title is used. title?: string; viewScope?: ViewScope; noContextMenu?: boolean; onContextMenu?: (e: MouseEvent) => void; } export default function NoteLink({ className, containerClassName, notePath, showNotePath, showNoteIcon, style, noPreview, noTnLink, highlightedTokens, title, viewScope, noContextMenu, onContextMenu }: NoteLinkOpts) { const stringifiedNotePath = Array.isArray(notePath) ? notePath.join("/") : notePath; const noteId = stringifiedNotePath.split("/").at(-1); const ref = useRef(null); const [ jqueryEl, setJqueryEl ] = useState>(); const highlightSearch = useImperativeSearchHighlighlighting(highlightedTokens); const [ noteTitle, setNoteTitle ] = useState(); useEffect(() => { link.createLink(stringifiedNotePath, { title, showNotePath, showNoteIcon, viewScope }).then(setJqueryEl); }, [ stringifiedNotePath, showNotePath, title, viewScope, noteTitle ]); useEffect(() => { const el = jqueryEl?.[0]; if (!el || !onContextMenu) return; el.addEventListener("contextmenu", onContextMenu); return () => el.removeEventListener("contextmenu", onContextMenu); }, [ jqueryEl, onContextMenu ]); useEffect(() => { if (!ref.current || !jqueryEl) return; ref.current.replaceChildren(jqueryEl[0]); highlightSearch(ref.current); }, [ jqueryEl, highlightedTokens ]); useTriliumEvent("entitiesReloaded", ({ loadResults }) => { // React to note title changes, but only if the title is not overwritten. if (!title && noteId) { const entityRow = loadResults.getEntityRow("notes", noteId); if (entityRow) { setNoteTitle(entityRow.title); } } }); if (style) { jqueryEl?.css(style); } const $linkEl = jqueryEl?.find("a"); if (noPreview) { $linkEl?.addClass("no-tooltip-preview"); } if (!noTnLink) { $linkEl?.addClass("tn-link"); } if (noContextMenu) { $linkEl?.attr("data-no-context-menu", "true"); } if (className) { $linkEl?.addClass(className); } return ; } interface NewNoteLinkProps extends Pick, "onContextMenu"> { className?: string; notePath: string; viewScope?: ViewScope; noContextMenu?: boolean; showNoteIcon?: boolean; noPreview?: boolean; } export function NewNoteLink({ notePath, viewScope, noContextMenu, showNoteIcon, noPreview, ...linkProps }: NewNoteLinkProps) { const noteId = notePath.split("/").at(-1); const note = useNote(noteId); const title = useNoteProperty(note, "title"); const icon = useNoteIcon(showNoteIcon ? note : null); const colorClass = useNoteColorClass(note); const [ archived ] = useNoteLabelBoolean(note, "archived"); return ( {icon && } {title} ); }