diff --git a/apps/client/src/widgets/react/hooks.tsx b/apps/client/src/widgets/react/hooks.tsx index 2b222fee9..be2842759 100644 --- a/apps/client/src/widgets/react/hooks.tsx +++ b/apps/client/src/widgets/react/hooks.tsx @@ -345,6 +345,30 @@ export function useNoteProperty(note: FNote | null | unde return note[property]; } +export function useNoteRelation(note: FNote | undefined | null, relationName: string): [string | null | undefined, (newValue: string) => void] { + const [ relationValue, setRelationValue ] = useState(note?.getRelationValue(relationName)); + + useEffect(() => setRelationValue(note?.getRelationValue(relationName) ?? null), [ note ]); + useTriliumEventBeta("entitiesReloaded", ({ loadResults }) => { + for (const attr of loadResults.getAttributeRows()) { + if (attr.type === "relation" && attr.name === relationName && attributes.isAffecting(attr, note)) { + setRelationValue(attr.value ?? null); + } + } + }); + + const setter = useCallback((value: string | undefined) => { + if (note) { + attributes.setAttribute(note, "relation", relationName, value) + } + }, [note]); + + return [ + relationValue, + setter + ] as const; +} + export function useNoteLabel(note: FNote | undefined | null, labelName: string): [string | null | undefined, (newValue: string) => void] { const [ labelValue, setLabelValue ] = useState(note?.getLabelValue(labelName)); diff --git a/apps/client/src/widgets/ribbon/SearchDefinitionTab.tsx b/apps/client/src/widgets/ribbon/SearchDefinitionTab.tsx index d1e41d108..55493a6f7 100644 --- a/apps/client/src/widgets/ribbon/SearchDefinitionTab.tsx +++ b/apps/client/src/widgets/ribbon/SearchDefinitionTab.tsx @@ -5,20 +5,19 @@ import { TabContext } from "./ribbon-interface"; import Dropdown from "../react/Dropdown"; import ActionButton from "../react/ActionButton"; import FormTextArea from "../react/FormTextArea"; -import { AttributeType, OptionNames, SaveSearchNoteResponse } from "@triliumnext/commons"; +import { AttributeType, SaveSearchNoteResponse } from "@triliumnext/commons"; import attributes, { removeOwnedAttributesByNameOrType } from "../../services/attributes"; -import { note } from "mermaid/dist/rendering-util/rendering-elements/shapes/note.js"; import FNote from "../../entities/fnote"; import toast from "../../services/toast"; import froca from "../../services/froca"; import { useContext, useEffect, useRef, useState } from "preact/hooks"; import { ParentComponent } from "../react/react_utils"; -import { useNoteLabel, useSpacedUpdate, useTooltip, useTriliumEventBeta } from "../react/hooks"; +import { useNoteLabel, useNoteRelation, useSpacedUpdate, useTooltip, useTriliumEventBeta } from "../react/hooks"; import appContext from "../../components/app_context"; import server from "../../services/server"; -import { tooltip } from "leaflet"; import ws from "../../services/ws"; import tree from "../../services/tree"; +import NoteAutocomplete from "../react/NoteAutocomplete"; interface SearchOption { attributeName: string; @@ -50,7 +49,8 @@ const SEARCH_OPTIONS: SearchOption[] = [ attributeName: "searchScript", attributeType: "relation", icon: "bx bx-code", - label: t("search_definition.search_script") + label: t("search_definition.search_script"), + component: SearchScriptOption }, { attributeName: "ancestor", @@ -158,7 +158,10 @@ export default function SearchDefinitionTab({ note, ntxId }: TabContext) { icon={icon} text={label} title={tooltip} - onClick={() => attributes.setAttribute(note, attributeType, attributeName, "")} + onClick={() => { + const defaultValue = (attributeType === "relation" ? "root" : ""); + attributes.setAttribute(note, attributeType, attributeName, defaultValue); + }} /> ))} @@ -324,4 +327,27 @@ function SearchStringOption({ note, refreshResults, error, ...restProps }: Searc }} /> +} + +function SearchScriptOption({ note, ...restProps }: SearchOptionProps) { + const [ searchScript, setSearchScript ] = useNoteRelation(note, "searchScript"); + + return +

{t("search_script.description1")}

+

{t("search_script.description2")}

+

{t("search_script.example_title")}

+
{t("search_script.example_code")}
+ {t("search_script.note")} + } + note={note} + {...restProps} + > + setSearchScript(noteId ?? "root")} + placeholder={t("search_script.placeholder")} + /> +
} \ No newline at end of file diff --git a/apps/client/src/widgets/search_options/search_script.ts b/apps/client/src/widgets/search_options/search_script.ts deleted file mode 100644 index 931886424..000000000 --- a/apps/client/src/widgets/search_options/search_script.ts +++ /dev/null @@ -1,62 +0,0 @@ -import AbstractSearchOption from "./abstract_search_option.js"; -import noteAutocompleteService from "../../services/note_autocomplete.js"; -import { t } from "../../services/i18n.js"; - -const TPL = /*html*/` - - - ${t("search_script.title")} - - -
- -
- - - - - - -`; - -export default class SearchScript extends AbstractSearchOption { - - static async create(noteId: string) { - await AbstractSearchOption.setAttribute(noteId, "relation", "searchScript", "root"); - } - - doRender() { - const $option = $(TPL); - const $searchScript = $option.find(".search-script"); - noteAutocompleteService.initNoteAutocomplete($searchScript, { allowCreatingNotes: true }); - - $searchScript.on("autocomplete:closed", async () => { - const searchScriptNoteId = $searchScript.getSelectedNoteId(); - - if (searchScriptNoteId) { - await this.setAttribute("relation", "searchScript", searchScriptNoteId); - } - }); - - const searchScriptNoteId = this.note.getRelationValue("searchScript"); - - if (searchScriptNoteId && searchScriptNoteId !== "root") { - $searchScript.setNote(searchScriptNoteId); - } - - return $option; - } -}