mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-03 20:06:08 +01:00 
			
		
		
		
	chore(react/ribbon): working execute search button
This commit is contained in:
		@@ -848,7 +848,7 @@
 | 
			
		||||
    "debug": "调试",
 | 
			
		||||
    "debug_description": "调试将打印额外的调试信息到控制台,以帮助调试复杂查询",
 | 
			
		||||
    "action": "操作",
 | 
			
		||||
    "search_button": "搜索 <kbd>回车</kbd>",
 | 
			
		||||
    "search_button": "搜索",
 | 
			
		||||
    "search_execute": "搜索并执行操作",
 | 
			
		||||
    "save_to_note": "保存到笔记",
 | 
			
		||||
    "search_parameters": "搜索参数",
 | 
			
		||||
 
 | 
			
		||||
@@ -828,7 +828,7 @@
 | 
			
		||||
    "debug": "debuggen",
 | 
			
		||||
    "debug_description": "Debug gibt zusätzliche Debuginformationen in die Konsole aus, um das Debuggen komplexer Abfragen zu erleichtern",
 | 
			
		||||
    "action": "Aktion",
 | 
			
		||||
    "search_button": "Suchen <kbd>Eingabetaste</kbd>",
 | 
			
		||||
    "search_button": "Suchen",
 | 
			
		||||
    "search_execute": "Aktionen suchen und ausführen",
 | 
			
		||||
    "save_to_note": "Als Notiz speichern",
 | 
			
		||||
    "search_parameters": "Suchparameter",
 | 
			
		||||
 
 | 
			
		||||
@@ -848,7 +848,7 @@
 | 
			
		||||
    "debug": "debug",
 | 
			
		||||
    "debug_description": "Debug will print extra debugging information into the console to aid in debugging complex queries",
 | 
			
		||||
    "action": "action",
 | 
			
		||||
    "search_button": "Search <kbd>enter</kbd>",
 | 
			
		||||
    "search_button": "Search",
 | 
			
		||||
    "search_execute": "Search & Execute actions",
 | 
			
		||||
    "save_to_note": "Save to note",
 | 
			
		||||
    "search_parameters": "Search Parameters",
 | 
			
		||||
 
 | 
			
		||||
@@ -848,7 +848,7 @@
 | 
			
		||||
    "debug": "depurar",
 | 
			
		||||
    "debug_description": "La depuración imprimirá información de depuración adicional en la consola para ayudar a depurar consultas complejas",
 | 
			
		||||
    "action": "acción",
 | 
			
		||||
    "search_button": "Buscar <kbd>Enter</kbd>",
 | 
			
		||||
    "search_button": "Buscar",
 | 
			
		||||
    "search_execute": "Buscar y ejecutar acciones",
 | 
			
		||||
    "save_to_note": "Guardar en nota",
 | 
			
		||||
    "search_parameters": "Parámetros de búsqueda",
 | 
			
		||||
 
 | 
			
		||||
@@ -848,7 +848,7 @@
 | 
			
		||||
    "debug": "debug",
 | 
			
		||||
    "debug_description": "Debug imprimera des informations supplémentaires dans la console pour faciliter le débogage des requêtes complexes",
 | 
			
		||||
    "action": "action",
 | 
			
		||||
    "search_button": "Recherche <kbd>Entrée</kbd>",
 | 
			
		||||
    "search_button": "Recherche",
 | 
			
		||||
    "search_execute": "Rechercher et exécuter des actions",
 | 
			
		||||
    "save_to_note": "Enregistrer dans la note",
 | 
			
		||||
    "search_parameters": "Paramètres de recherche",
 | 
			
		||||
 
 | 
			
		||||
@@ -185,7 +185,7 @@
 | 
			
		||||
    "debug": "デバッグ",
 | 
			
		||||
    "debug_description": "デバッグは複雑なクエリのデバッグを支援するために、追加のデバッグ情報をコンソールに出力します",
 | 
			
		||||
    "action": "アクション",
 | 
			
		||||
    "search_button": "検索 <kbd>Enter</kbd>",
 | 
			
		||||
    "search_button": "検索",
 | 
			
		||||
    "search_execute": "検索とアクションの実行",
 | 
			
		||||
    "save_to_note": "ノートに保存",
 | 
			
		||||
    "search_parameters": "検索パラメータ",
 | 
			
		||||
 
 | 
			
		||||
@@ -1006,7 +1006,7 @@
 | 
			
		||||
    "limit_description": "Limitar número de resultados",
 | 
			
		||||
    "debug": "depurar",
 | 
			
		||||
    "action": "ação",
 | 
			
		||||
    "search_button": "Pesquisar <kbd>enter</kbd>",
 | 
			
		||||
    "search_button": "Pesquisar",
 | 
			
		||||
    "search_execute": "Pesquisar & Executar ações",
 | 
			
		||||
    "save_to_note": "Salvar para nota",
 | 
			
		||||
    "search_parameters": "Parâmetros de Pesquisa",
 | 
			
		||||
 
 | 
			
		||||
@@ -1106,7 +1106,7 @@
 | 
			
		||||
    "limit_description": "Limitează numărul de rezultate",
 | 
			
		||||
    "order_by": "ordonează după",
 | 
			
		||||
    "save_to_note": "Salvează în notiță",
 | 
			
		||||
    "search_button": "Căutare <kbd>Enter</kbd>",
 | 
			
		||||
    "search_button": "Căutare",
 | 
			
		||||
    "search_execute": "Caută și execută acțiunile",
 | 
			
		||||
    "search_note_saved": "Notița de căutare a fost salvată în {{- notePathTitle}}",
 | 
			
		||||
    "search_parameters": "Parametrii de căutare",
 | 
			
		||||
 
 | 
			
		||||
@@ -1060,7 +1060,7 @@
 | 
			
		||||
    "fast_search": "быстрый поиск",
 | 
			
		||||
    "include_archived": "включать архивированные",
 | 
			
		||||
    "order_by": "сортировать по",
 | 
			
		||||
    "search_button": "Поиск <kbd>enter</kbd>",
 | 
			
		||||
    "search_button": "Поиск",
 | 
			
		||||
    "search_parameters": "Параметры поиска",
 | 
			
		||||
    "ancestor": "предок",
 | 
			
		||||
    "action": "действие",
 | 
			
		||||
 
 | 
			
		||||
@@ -845,7 +845,7 @@
 | 
			
		||||
    "debug": "除錯",
 | 
			
		||||
    "debug_description": "除錯將顯示額外的除錯資訊至控制台,以幫助除錯複雜查詢",
 | 
			
		||||
    "action": "操作",
 | 
			
		||||
    "search_button": "搜尋 <kbd>Enter</kbd>",
 | 
			
		||||
    "search_button": "搜尋",
 | 
			
		||||
    "search_execute": "搜尋並執行操作",
 | 
			
		||||
    "save_to_note": "儲存至筆記",
 | 
			
		||||
    "search_parameters": "搜尋參數",
 | 
			
		||||
 
 | 
			
		||||
@@ -960,7 +960,7 @@
 | 
			
		||||
    "debug": "debug",
 | 
			
		||||
    "debug_description": "Debug виведе додаткову інформацію для налагодження в консоль, щоб допомогти у налагодженні складних запитів",
 | 
			
		||||
    "action": "дія",
 | 
			
		||||
    "search_button": "Пошук <kbd>enter</kbd>",
 | 
			
		||||
    "search_button": "Пошук",
 | 
			
		||||
    "search_execute": "Пошук & Виконання дій",
 | 
			
		||||
    "save_to_note": "Зберегти до нотатки",
 | 
			
		||||
    "search_parameters": "Параметри пошуку",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,22 +1,26 @@
 | 
			
		||||
interface FormTextAreaProps {
 | 
			
		||||
import { TextareaHTMLAttributes } from "preact/compat";
 | 
			
		||||
 | 
			
		||||
interface FormTextAreaProps extends Omit<TextareaHTMLAttributes, "onBlur" | "onChange"> {
 | 
			
		||||
    id?: string;
 | 
			
		||||
    currentValue: string;
 | 
			
		||||
    onChange?(newValue: string): void;
 | 
			
		||||
    onBlur?(newValue: string): void;
 | 
			
		||||
    rows: number;
 | 
			
		||||
    className?: string;
 | 
			
		||||
    placeholder?: string;
 | 
			
		||||
}
 | 
			
		||||
export default function FormTextArea({ id, onBlur, rows, currentValue, className, placeholder }: FormTextAreaProps) {
 | 
			
		||||
export default function FormTextArea({ id, onBlur, onChange, rows, currentValue, className, ...restProps }: FormTextAreaProps) {
 | 
			
		||||
    return (
 | 
			
		||||
        <textarea
 | 
			
		||||
            id={id}
 | 
			
		||||
            rows={rows}
 | 
			
		||||
            className={`form-control ${className ?? ""}`}
 | 
			
		||||
            onChange={(e) => {
 | 
			
		||||
                onChange?.(e.currentTarget.value);
 | 
			
		||||
            }}
 | 
			
		||||
            onBlur={(e) => {
 | 
			
		||||
                onBlur?.(e.currentTarget.value);
 | 
			
		||||
            }}
 | 
			
		||||
            style={{ width: "100%" }}
 | 
			
		||||
            placeholder={placeholder}
 | 
			
		||||
            {...restProps}
 | 
			
		||||
        >{currentValue}</textarea>
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -109,7 +109,7 @@ export function useTriliumEventBeta<T extends EventNames>(eventName: T | T[], ha
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function useSpacedUpdate(callback: () => Promise<void>, interval = 1000) {
 | 
			
		||||
export function useSpacedUpdate(callback: () => void | Promise<void>, interval = 1000) {
 | 
			
		||||
    const callbackRef = useRef(callback);
 | 
			
		||||
    const spacedUpdateRef = useRef<SpacedUpdate>();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,9 +6,16 @@ import Dropdown from "../react/Dropdown";
 | 
			
		||||
import ActionButton from "../react/ActionButton";
 | 
			
		||||
import FormTextArea from "../react/FormTextArea";
 | 
			
		||||
import { AttributeType, OptionNames } from "@triliumnext/commons";
 | 
			
		||||
import { removeOwnedAttributesByNameOrType } from "../../services/attributes";
 | 
			
		||||
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, useRef } from "preact/hooks";
 | 
			
		||||
import { ParentComponent } from "../react/react_utils";
 | 
			
		||||
import { useSpacedUpdate } from "../react/hooks";
 | 
			
		||||
import appContext from "../../components/app_context";
 | 
			
		||||
import server from "../../services/server";
 | 
			
		||||
 | 
			
		||||
interface SearchOption {
 | 
			
		||||
  searchOption: string;
 | 
			
		||||
@@ -64,7 +71,29 @@ const SEARCH_OPTIONS: SearchOption[] = [
 | 
			
		||||
  }
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export default function SearchDefinitionTab({ note }: TabContext) {
 | 
			
		||||
export default function SearchDefinitionTab({ note, ntxId }: TabContext) {
 | 
			
		||||
  const parentComponent = useContext(ParentComponent);
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  async function refreshResults() {
 | 
			
		||||
    const noteId = note?.noteId;
 | 
			
		||||
    if (!noteId) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        const result = await froca.loadSearchNote(noteId);
 | 
			
		||||
 | 
			
		||||
        if (result && result.error) {
 | 
			
		||||
          //this.handleEvent("showSearchError", { error: result.error });
 | 
			
		||||
        }
 | 
			
		||||
    } catch (e: any) {
 | 
			
		||||
        toast.showError(e.message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    parentComponent?.triggerEvent("searchRefreshed", { ntxId });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="search-definition-widget">
 | 
			
		||||
      <div className="search-settings">
 | 
			
		||||
@@ -82,7 +111,27 @@ export default function SearchDefinitionTab({ note }: TabContext) {
 | 
			
		||||
            </td>
 | 
			
		||||
          </tr>
 | 
			
		||||
          <tbody className="search-options">
 | 
			
		||||
            <SearchStringOption />
 | 
			
		||||
            <SearchStringOption
 | 
			
		||||
              refreshResults={refreshResults}
 | 
			
		||||
              note={note}
 | 
			
		||||
            />
 | 
			
		||||
          </tbody>
 | 
			
		||||
          <tbody className="action-options">
 | 
			
		||||
 | 
			
		||||
          </tbody>
 | 
			
		||||
          <tbody>
 | 
			
		||||
            <tr>
 | 
			
		||||
              <td colSpan={3}>
 | 
			
		||||
                <div style={{ display: "flex", justifyContent: "space-evenly" }}>
 | 
			
		||||
                  <Button
 | 
			
		||||
                    icon="bx bx-search"
 | 
			
		||||
                    text={t("search_definition.search_button")}
 | 
			
		||||
                    keyboardShortcut="Enter"
 | 
			
		||||
                    onClick={refreshResults}
 | 
			
		||||
                  />
 | 
			
		||||
                </div>
 | 
			
		||||
              </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
          </tbody>
 | 
			
		||||
        </table>
 | 
			
		||||
      </div>
 | 
			
		||||
@@ -114,7 +163,21 @@ function SearchOption({ note, title, children, help, attributeName, attributeTyp
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function SearchStringOption() {
 | 
			
		||||
function SearchStringOption({ note, refreshResults }: { note: FNote, refreshResults: () => void }) {
 | 
			
		||||
  const currentValue = useRef("");
 | 
			
		||||
  const spacedUpdate = useSpacedUpdate(async () => {
 | 
			
		||||
    const searchString = currentValue.current;
 | 
			
		||||
    appContext.lastSearchString = searchString;
 | 
			
		||||
 | 
			
		||||
    await attributes.setAttribute(note, "label", "searchString", searchString);
 | 
			
		||||
 | 
			
		||||
    if (note.title.startsWith(t("search_string.search_prefix"))) {
 | 
			
		||||
      await server.put(`notes/${note.noteId}/title`, {
 | 
			
		||||
          title: `${t("search_string.search_prefix")} ${searchString.length < 30 ? searchString : `${searchString.substr(0, 30)}…`}`
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }, 1000);
 | 
			
		||||
 | 
			
		||||
  return <SearchOption
 | 
			
		||||
    title={t("search_string.title_column")}
 | 
			
		||||
    help={<>
 | 
			
		||||
@@ -133,6 +196,21 @@ function SearchStringOption() {
 | 
			
		||||
    <FormTextArea
 | 
			
		||||
      className="search-string"
 | 
			
		||||
      placeholder={t("search_string.placeholder")}
 | 
			
		||||
      onChange={text => {
 | 
			
		||||
        currentValue.current = text;
 | 
			
		||||
        spacedUpdate.scheduleUpdate();
 | 
			
		||||
      }}
 | 
			
		||||
      onKeyDown={async (e) => {
 | 
			
		||||
        if (e.key === "Enter") {
 | 
			
		||||
          e.preventDefault();
 | 
			
		||||
 | 
			
		||||
          // this also in effect disallows new lines in query string.
 | 
			
		||||
          // on one hand, this makes sense since search string is a label
 | 
			
		||||
          // on the other hand, it could be nice for structuring long search string. It's probably a niche case though.
 | 
			
		||||
          await spacedUpdate.updateNowIfNecessary();
 | 
			
		||||
          refreshResults();
 | 
			
		||||
        }
 | 
			
		||||
      }}
 | 
			
		||||
    />
 | 
			
		||||
  </SearchOption>
 | 
			
		||||
}
 | 
			
		||||
@@ -43,7 +43,7 @@ const TPL = /*html*/`
 | 
			
		||||
                        <div style="display: flex; justify-content: space-evenly">
 | 
			
		||||
                            <button type="button" class="btn btn-sm search-button">
 | 
			
		||||
                                <span class="bx bx-search"></span>
 | 
			
		||||
                                ${t("search_definition.search_button")}
 | 
			
		||||
                                ${}
 | 
			
		||||
                            </button>
 | 
			
		||||
 | 
			
		||||
                            <button type="button" class="btn btn-sm search-and-execute-button">
 | 
			
		||||
@@ -126,9 +126,6 @@ export default class SearchDefinitionWidget extends NoteContextAwareWidget {
 | 
			
		||||
        this.$searchOptions = this.$widget.find(".search-options");
 | 
			
		||||
        this.$actionOptions = this.$widget.find(".action-options");
 | 
			
		||||
 | 
			
		||||
        this.$searchButton = this.$widget.find(".search-button");
 | 
			
		||||
        this.$searchButton.on("click", () => this.triggerCommand("refreshResults"));
 | 
			
		||||
 | 
			
		||||
        this.$searchAndExecuteButton = this.$widget.find(".search-and-execute-button");
 | 
			
		||||
        this.$searchAndExecuteButton.on("click", () => this.searchAndExecute());
 | 
			
		||||
 | 
			
		||||
@@ -145,24 +142,6 @@ export default class SearchDefinitionWidget extends NoteContextAwareWidget {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async refreshResultsCommand() {
 | 
			
		||||
        if (!this.noteId) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            const result = await froca.loadSearchNote(this.noteId);
 | 
			
		||||
 | 
			
		||||
            if (result && result.error) {
 | 
			
		||||
                this.handleEvent("showSearchError", { error: result.error });
 | 
			
		||||
            }
 | 
			
		||||
        } catch (e: any) {
 | 
			
		||||
            toastService.showError(e.message);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.triggerEvent("searchRefreshed", { ntxId: this.noteContext?.ntxId });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async refreshWithNote(note: FNote) {
 | 
			
		||||
        if (!this.note) {
 | 
			
		||||
            return;
 | 
			
		||||
 
 | 
			
		||||
@@ -19,12 +19,6 @@ export default abstract class AbstractSearchOption extends Component {
 | 
			
		||||
        this.note = note;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static async setAttribute(noteId: string, type: AttributeType, name: string, value: string = "") {
 | 
			
		||||
        await server.put(`notes/${noteId}/set-attribute`, { type, name, value });
 | 
			
		||||
 | 
			
		||||
        await ws.waitForMaxKnownEntityChangeId();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async setAttribute(type: AttributeType, name: string, value: string = "") {
 | 
			
		||||
        // TODO: Find a better pattern.
 | 
			
		||||
        await (this.constructor as any).setAttribute(this.note.noteId, type, name, value);
 | 
			
		||||
 
 | 
			
		||||
@@ -25,28 +25,9 @@ export default class SearchString extends AbstractSearchOption {
 | 
			
		||||
    doRender() {
 | 
			
		||||
        const $option = $(TPL);
 | 
			
		||||
        this.$searchString = $option.find(".search-string");
 | 
			
		||||
        this.$searchString.on("input", () => this.spacedUpdate.scheduleUpdate());
 | 
			
		||||
 | 
			
		||||
        shortcutService.bindElShortcut(this.$searchString, "return", async () => {
 | 
			
		||||
            // this also in effect disallows new lines in query string.
 | 
			
		||||
            // on one hand, this makes sense since search string is a label
 | 
			
		||||
            // on the other hand, it could be nice for structuring long search string. It's probably a niche case though.
 | 
			
		||||
            await this.spacedUpdate.updateNowIfNecessary();
 | 
			
		||||
 | 
			
		||||
            this.triggerCommand("refreshResults");
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.spacedUpdate = new SpacedUpdate(async () => {
 | 
			
		||||
            const searchString = String(this.$searchString.val());
 | 
			
		||||
            appContext.lastSearchString = searchString;
 | 
			
		||||
 | 
			
		||||
            await this.setAttribute("label", "searchString", searchString);
 | 
			
		||||
 | 
			
		||||
            if (this.note.title.startsWith(t("search_string.search_prefix"))) {
 | 
			
		||||
                await server.put(`notes/${this.note.noteId}/title`, {
 | 
			
		||||
                    title: `${t("search_string.search_prefix")} ${searchString.length < 30 ? searchString : `${searchString.substr(0, 30)}…`}`
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }, 1000);
 | 
			
		||||
 | 
			
		||||
        this.$searchString.val(this.note.getLabelValue("searchString") ?? "");
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user