mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-04 05:28:59 +01:00 
			
		
		
		
	chore(client/ts): port services/note_autocomplete
This commit is contained in:
		
							parent
							
								
									934a395f15
								
							
						
					
					
						commit
						e889955e8b
					
				@ -40,6 +40,9 @@ export type TriggerData = {
 | 
			
		||||
    text: string;
 | 
			
		||||
} | {
 | 
			
		||||
    callback: (value: NoteDetailWidget | PromiseLike<NoteDetailWidget>) => void
 | 
			
		||||
} | {
 | 
			
		||||
    // For "searchNotes"
 | 
			
		||||
    searchString: string | undefined;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class AppContext extends Component {
 | 
			
		||||
 | 
			
		||||
@ -243,7 +243,7 @@ class FrocaImpl implements Froca {
 | 
			
		||||
        }).filter(note => !!note) as FNote[];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async getNotes(noteIds: string[], silentNotFoundError = false): Promise<FNote[]> {
 | 
			
		||||
    async getNotes(noteIds: string[] | JQuery<string>, silentNotFoundError = false): Promise<FNote[]> {
 | 
			
		||||
        noteIds = Array.from(new Set(noteIds)); // make unique
 | 
			
		||||
        const missingNoteIds = noteIds.filter(noteId => !this.notes[noteId]);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,26 @@ const SELECTED_NOTE_PATH_KEY = "data-note-path";
 | 
			
		||||
 | 
			
		||||
const SELECTED_EXTERNAL_LINK_KEY = "data-external-link";
 | 
			
		||||
 | 
			
		||||
async function autocompleteSourceForCKEditor(queryText) {
 | 
			
		||||
export interface Suggestion {
 | 
			
		||||
    noteTitle?: string;
 | 
			
		||||
    externalLink?: string;
 | 
			
		||||
    notePathTitle?: string;
 | 
			
		||||
    notePath?: string;
 | 
			
		||||
    highlightedNotePathTitle?: string;
 | 
			
		||||
    action?: string | "create-note" | "search-notes" | "external-link";
 | 
			
		||||
    parentNoteId?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface Options {
 | 
			
		||||
    container?: HTMLElement;
 | 
			
		||||
    fastSearch?: boolean;
 | 
			
		||||
    allowCreatingNotes?: boolean;
 | 
			
		||||
    allowJumpToSearchNotes?: boolean;
 | 
			
		||||
    allowExternalLinks?: boolean;
 | 
			
		||||
    hideGoToSelectedNoteButton?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function autocompleteSourceForCKEditor(queryText: string) {
 | 
			
		||||
    return await new Promise((res, rej) => {
 | 
			
		||||
        autocompleteSource(queryText, rows => {
 | 
			
		||||
            res(rows.map(row => {
 | 
			
		||||
@ -30,7 +49,7 @@ async function autocompleteSourceForCKEditor(queryText) {
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function autocompleteSource(term, cb, options = {}) {
 | 
			
		||||
async function autocompleteSource(term: string, cb: (rows: Suggestion[]) => void, options: Options = {}) {
 | 
			
		||||
    const fastSearch = options.fastSearch === false ? false : true;
 | 
			
		||||
    if (fastSearch === false) {
 | 
			
		||||
        if (term.trim().length === 0){
 | 
			
		||||
@ -46,7 +65,7 @@ async function autocompleteSource(term, cb, options = {}) {
 | 
			
		||||
    
 | 
			
		||||
    const activeNoteId = appContext.tabManager.getActiveContextNoteId();
 | 
			
		||||
 | 
			
		||||
    let results = await server.get(`autocomplete?query=${encodeURIComponent(term)}&activeNoteId=${activeNoteId}&fastSearch=${fastSearch}`);
 | 
			
		||||
    let results: Suggestion[] = await server.get<Suggestion[]>(`autocomplete?query=${encodeURIComponent(term)}&activeNoteId=${activeNoteId}&fastSearch=${fastSearch}`);
 | 
			
		||||
    if (term.trim().length >= 1 && options.allowCreatingNotes) {
 | 
			
		||||
        results = [
 | 
			
		||||
            {
 | 
			
		||||
@ -54,7 +73,7 @@ async function autocompleteSource(term, cb, options = {}) {
 | 
			
		||||
                noteTitle: term,
 | 
			
		||||
                parentNoteId: activeNoteId || 'root',
 | 
			
		||||
                highlightedNotePathTitle: t("note_autocomplete.create-note", { term })
 | 
			
		||||
            }
 | 
			
		||||
            } as Suggestion
 | 
			
		||||
        ].concat(results);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -74,14 +93,14 @@ async function autocompleteSource(term, cb, options = {}) {
 | 
			
		||||
                action: 'external-link',
 | 
			
		||||
                externalLink: term,
 | 
			
		||||
                highlightedNotePathTitle: t("note_autocomplete.insert-external-link", { term })
 | 
			
		||||
            }
 | 
			
		||||
            } as Suggestion
 | 
			
		||||
        ].concat(results);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cb(results);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function clearText($el) {
 | 
			
		||||
function clearText($el: JQuery<HTMLElement>) {
 | 
			
		||||
    if (utils.isMobile()) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
@ -90,7 +109,7 @@ function clearText($el) {
 | 
			
		||||
    $el.autocomplete("val", "").trigger('change');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function setText($el, text) {
 | 
			
		||||
function setText($el: JQuery<HTMLElement>, text: string) {
 | 
			
		||||
    if (utils.isMobile()) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
@ -101,7 +120,7 @@ function setText($el, text) {
 | 
			
		||||
        .autocomplete("open");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function showRecentNotes($el) {
 | 
			
		||||
function showRecentNotes($el:JQuery<HTMLElement>) {
 | 
			
		||||
    if (utils.isMobile()) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
@ -112,21 +131,22 @@ function showRecentNotes($el) {
 | 
			
		||||
    $el.trigger('focus');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function fullTextSearch($el, options){
 | 
			
		||||
    const searchString = $el.autocomplete('val');
 | 
			
		||||
    if (options.fastSearch === false || searchString.trim().length === 0) {
 | 
			
		||||
function fullTextSearch($el: JQuery<HTMLElement>, options: Options){
 | 
			
		||||
    const searchString = $el.autocomplete('val') as unknown as string;
 | 
			
		||||
    if (options.fastSearch === false || searchString?.trim().length === 0) {
 | 
			
		||||
        return;
 | 
			
		||||
    }    
 | 
			
		||||
    $el.trigger('focus');
 | 
			
		||||
    options.fastSearch = false;
 | 
			
		||||
    $el.autocomplete('val', '');
 | 
			
		||||
    $el.autocomplete()
 | 
			
		||||
    $el.setSelectedNotePath("");
 | 
			
		||||
    $el.autocomplete('val', searchString);
 | 
			
		||||
    // Set a delay to avoid resetting to true before full text search (await server.get) is called.
 | 
			
		||||
    setTimeout(() => { options.fastSearch = true; }, 100);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function initNoteAutocomplete($el, options) {
 | 
			
		||||
function initNoteAutocomplete($el: JQuery<HTMLElement>, options: Options) {
 | 
			
		||||
    if ($el.hasClass("note-autocomplete-input") || utils.isMobile()) {
 | 
			
		||||
        // clear any event listener added in previous invocation of this function
 | 
			
		||||
        $el.off('autocomplete:noteselected');
 | 
			
		||||
@ -174,7 +194,7 @@ function initNoteAutocomplete($el, options) {
 | 
			
		||||
        return false;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let autocompleteOptions = {};
 | 
			
		||||
    let autocompleteOptions: AutoCompleteConfig = {};
 | 
			
		||||
    if (options.container) {
 | 
			
		||||
        autocompleteOptions.dropdownMenuContainer = options.container;
 | 
			
		||||
        autocompleteOptions.debug = true;   // don't close on blur
 | 
			
		||||
@ -221,7 +241,8 @@ function initNoteAutocomplete($el, options) {
 | 
			
		||||
        }
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    $el.on('autocomplete:selected', async (event, suggestion) => {
 | 
			
		||||
    // TODO: Types fail due to "autocomplete:selected" not being registered in type definitions.
 | 
			
		||||
    ($el as any).on('autocomplete:selected', async (event: Event, suggestion: Suggestion) => {
 | 
			
		||||
        if (suggestion.action === 'external-link') {
 | 
			
		||||
            $el.setSelectedNotePath(null);
 | 
			
		||||
            $el.setSelectedExternalLink(suggestion.externalLink);
 | 
			
		||||
@ -250,7 +271,7 @@ function initNoteAutocomplete($el, options) {
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            const hoistedNoteId = appContext.tabManager.getActiveContext()?.hoistedNoteId;
 | 
			
		||||
            suggestion.notePath = note.getBestNotePathString(hoistedNoteId);
 | 
			
		||||
            suggestion.notePath = note?.getBestNotePathString(hoistedNoteId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (suggestion.action === 'search-notes') {
 | 
			
		||||
@ -270,7 +291,7 @@ function initNoteAutocomplete($el, options) {
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    $el.on('autocomplete:closed', () => {
 | 
			
		||||
        if (!$el.val().trim()) {
 | 
			
		||||
        if (!String($el.val())?.trim()) {
 | 
			
		||||
            clearText($el);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
@ -289,7 +310,7 @@ function initNoteAutocomplete($el, options) {
 | 
			
		||||
 | 
			
		||||
function init() {
 | 
			
		||||
    $.fn.getSelectedNotePath = function () {
 | 
			
		||||
        if (!$(this).val().trim()) {
 | 
			
		||||
        if (!String($(this).val())?.trim()) {
 | 
			
		||||
            return "";
 | 
			
		||||
        } else {
 | 
			
		||||
            return $(this).attr(SELECTED_NOTE_PATH_KEY);
 | 
			
		||||
@ -297,7 +318,8 @@ function init() {
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $.fn.getSelectedNoteId = function () {
 | 
			
		||||
        const notePath = $(this).getSelectedNotePath();
 | 
			
		||||
        const $el = $(this as unknown as HTMLElement);
 | 
			
		||||
        const notePath = $el.getSelectedNotePath();
 | 
			
		||||
        if (!notePath) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
@ -320,7 +342,7 @@ function init() {
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    $.fn.getSelectedExternalLink = function () {
 | 
			
		||||
        if (!$(this).val().trim()) {
 | 
			
		||||
        if (!String($(this).val())?.trim()) {
 | 
			
		||||
            return "";
 | 
			
		||||
        } else {
 | 
			
		||||
            return $(this).attr(SELECTED_EXTERNAL_LINK_KEY);
 | 
			
		||||
@ -329,6 +351,7 @@ function init() {
 | 
			
		||||
 | 
			
		||||
    $.fn.setSelectedExternalLink = function (externalLink) {
 | 
			
		||||
        if (externalLink) {
 | 
			
		||||
            // TODO: This doesn't seem to do anything with the external link, is it normal?
 | 
			
		||||
            $(this)
 | 
			
		||||
                .closest(".input-group")
 | 
			
		||||
                .find(".go-to-selected-note-button")
 | 
			
		||||
							
								
								
									
										27
									
								
								src/public/app/types.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								src/public/app/types.d.ts
									
									
									
									
										vendored
									
									
								
							@ -2,6 +2,7 @@ import type FNote from "./entities/fnote";
 | 
			
		||||
import type { BackendModule, i18n } from "i18next";
 | 
			
		||||
import type { Froca } from "./services/froca-interface";
 | 
			
		||||
import type { HttpBackendOptions } from "i18next-http-backend";
 | 
			
		||||
import { Suggestion } from "./services/note_autocomplete.ts";
 | 
			
		||||
 | 
			
		||||
interface ElectronProcess {
 | 
			
		||||
    type: string;
 | 
			
		||||
@ -53,19 +54,32 @@ declare global {
 | 
			
		||||
        hint?: boolean;
 | 
			
		||||
        openOnFocus?: boolean;
 | 
			
		||||
        minLength?: number;
 | 
			
		||||
        tabAutocomplete?: boolean
 | 
			
		||||
        tabAutocomplete?: boolean;
 | 
			
		||||
        autoselect?: boolean;
 | 
			
		||||
        dropdownMenuContainer?: HTMLElement;
 | 
			
		||||
        debug?: boolean;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    type AutoCompleteCallback = (values: AutoCompleteCallbackArgs[]) => void;
 | 
			
		||||
    type AutoCompleteCallback = (values: AutoCompleteCallbackArg[]) => void;
 | 
			
		||||
 | 
			
		||||
    interface AutoCompleteArg {
 | 
			
		||||
        displayKey: "name" | "value";
 | 
			
		||||
        displayKey: "name" | "value" | "notePathTitle";
 | 
			
		||||
        cache: boolean;
 | 
			
		||||
        source: (term: string, cb: AutoCompleteCallback) => void
 | 
			
		||||
        source: (term: string, cb: AutoCompleteCallback) => void,
 | 
			
		||||
        templates: {
 | 
			
		||||
            suggestion: (suggestion: Suggestion) => string | undefined
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    interface JQuery {
 | 
			
		||||
        autocomplete: (action: "close" | "open" | "destroy" | AutoCompleteConfig, args?: AutoCompleteArg[]) => void;        
 | 
			
		||||
        autocomplete: (action?: "close" | "open" | "destroy" | "val" | AutoCompleteConfig, args?: AutoCompleteArg[] | string) => JQuery<?>;
 | 
			
		||||
        
 | 
			
		||||
        getSelectedNotePath(): string | undefined;
 | 
			
		||||
        getSelectedNoteId(): string | null;
 | 
			
		||||
        setSelectedNotePath(notePath: string | null | undefined);
 | 
			
		||||
        getSelectedExternalLink(this: HTMLElement): string | undefined;
 | 
			
		||||
        setSelectedExternalLink(externalLink: string | null | undefined);
 | 
			
		||||
        setNote(noteId: string);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var logError: (message: string) => void;
 | 
			
		||||
@ -92,4 +106,7 @@ declare global {
 | 
			
		||||
    }) => {
 | 
			
		||||
        destroy();
 | 
			
		||||
    };
 | 
			
		||||
    var renderMathInElement: (element: HTMLElement, options: {
 | 
			
		||||
        trust: boolean;
 | 
			
		||||
    }) => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user