mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-04 13:39:01 +01:00 
			
		
		
		
	feat(react/dialogs): port include_note
This commit is contained in:
		
							parent
							
								
									7202f47716
								
							
						
					
					
						commit
						0cfe3351bb
					
				@ -202,7 +202,7 @@
 | 
				
			|||||||
    "box_size_small": "小型 (显示大约10行)",
 | 
					    "box_size_small": "小型 (显示大约10行)",
 | 
				
			||||||
    "box_size_medium": "中型 (显示大约30行)",
 | 
					    "box_size_medium": "中型 (显示大约30行)",
 | 
				
			||||||
    "box_size_full": "完整显示(完整文本框)",
 | 
					    "box_size_full": "完整显示(完整文本框)",
 | 
				
			||||||
    "button_include": "包含笔记 <kbd>回车</kbd>"
 | 
					    "button_include": "包含笔记"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "info": {
 | 
					  "info": {
 | 
				
			||||||
    "modalTitle": "信息消息",
 | 
					    "modalTitle": "信息消息",
 | 
				
			||||||
 | 
				
			|||||||
@ -201,7 +201,7 @@
 | 
				
			|||||||
    "box_size_small": "klein (~ 10 Zeilen)",
 | 
					    "box_size_small": "klein (~ 10 Zeilen)",
 | 
				
			||||||
    "box_size_medium": "mittel (~ 30 Zeilen)",
 | 
					    "box_size_medium": "mittel (~ 30 Zeilen)",
 | 
				
			||||||
    "box_size_full": "vollständig (Feld zeigt vollständigen Text)",
 | 
					    "box_size_full": "vollständig (Feld zeigt vollständigen Text)",
 | 
				
			||||||
    "button_include": "Notiz beifügen <kbd>Eingabetaste</kbd>"
 | 
					    "button_include": "Notiz beifügen"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "info": {
 | 
					  "info": {
 | 
				
			||||||
    "modalTitle": "Infonachricht",
 | 
					    "modalTitle": "Infonachricht",
 | 
				
			||||||
 | 
				
			|||||||
@ -203,7 +203,7 @@
 | 
				
			|||||||
    "box_size_small": "small (~ 10 lines)",
 | 
					    "box_size_small": "small (~ 10 lines)",
 | 
				
			||||||
    "box_size_medium": "medium (~ 30 lines)",
 | 
					    "box_size_medium": "medium (~ 30 lines)",
 | 
				
			||||||
    "box_size_full": "full (box shows complete text)",
 | 
					    "box_size_full": "full (box shows complete text)",
 | 
				
			||||||
    "button_include": "Include note <kbd>enter</kbd>"
 | 
					    "button_include": "Include note"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "info": {
 | 
					  "info": {
 | 
				
			||||||
    "modalTitle": "Info message",
 | 
					    "modalTitle": "Info message",
 | 
				
			||||||
 | 
				
			|||||||
@ -202,7 +202,7 @@
 | 
				
			|||||||
    "box_size_small": "pequeño (~ 10 líneas)",
 | 
					    "box_size_small": "pequeño (~ 10 líneas)",
 | 
				
			||||||
    "box_size_medium": "medio (~ 30 líneas)",
 | 
					    "box_size_medium": "medio (~ 30 líneas)",
 | 
				
			||||||
    "box_size_full": "completo (el cuadro muestra el texto completo)",
 | 
					    "box_size_full": "completo (el cuadro muestra el texto completo)",
 | 
				
			||||||
    "button_include": "Incluir nota <kbd>Enter</kbd>"
 | 
					    "button_include": "Incluir nota"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "info": {
 | 
					  "info": {
 | 
				
			||||||
    "modalTitle": "Mensaje informativo",
 | 
					    "modalTitle": "Mensaje informativo",
 | 
				
			||||||
 | 
				
			|||||||
@ -201,7 +201,7 @@
 | 
				
			|||||||
    "box_size_small": "petit (~ 10 lignes)",
 | 
					    "box_size_small": "petit (~ 10 lignes)",
 | 
				
			||||||
    "box_size_medium": "moyen (~ 30 lignes)",
 | 
					    "box_size_medium": "moyen (~ 30 lignes)",
 | 
				
			||||||
    "box_size_full": "complet (la boîte affiche le texte complet)",
 | 
					    "box_size_full": "complet (la boîte affiche le texte complet)",
 | 
				
			||||||
    "button_include": "Inclure une note <kbd>Entrée</kbd>"
 | 
					    "button_include": "Inclure une note"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "info": {
 | 
					  "info": {
 | 
				
			||||||
    "modalTitle": "Message d'information",
 | 
					    "modalTitle": "Message d'information",
 | 
				
			||||||
 | 
				
			|||||||
@ -750,7 +750,7 @@
 | 
				
			|||||||
    "box_size_medium": "mediu (~ 30 de rânduri)",
 | 
					    "box_size_medium": "mediu (~ 30 de rânduri)",
 | 
				
			||||||
    "box_size_prompt": "Dimensiunea căsuței notiței incluse:",
 | 
					    "box_size_prompt": "Dimensiunea căsuței notiței incluse:",
 | 
				
			||||||
    "box_size_small": "mică (~ 10 rânduri)",
 | 
					    "box_size_small": "mică (~ 10 rânduri)",
 | 
				
			||||||
    "button_include": "Include notița <kbd>Enter</kbd>",
 | 
					    "button_include": "Include notița",
 | 
				
			||||||
    "dialog_title": "Includere notița",
 | 
					    "dialog_title": "Includere notița",
 | 
				
			||||||
    "label_note": "Notiță",
 | 
					    "label_note": "Notiță",
 | 
				
			||||||
    "placeholder_search": "căutați notița după denumirea ei",
 | 
					    "placeholder_search": "căutați notița după denumirea ei",
 | 
				
			||||||
 | 
				
			|||||||
@ -203,7 +203,7 @@
 | 
				
			|||||||
    "box_size_small": "mala (~ 10 redova)",
 | 
					    "box_size_small": "mala (~ 10 redova)",
 | 
				
			||||||
    "box_size_medium": "srednja (~ 30 redova)",
 | 
					    "box_size_medium": "srednja (~ 30 redova)",
 | 
				
			||||||
    "box_size_full": "puna (kutija prikazuje ceo tekst)",
 | 
					    "box_size_full": "puna (kutija prikazuje ceo tekst)",
 | 
				
			||||||
    "button_include": "Uključi belešku <kbd>enter</kbd>"
 | 
					    "button_include": "Uključi belešku"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "info": {
 | 
					  "info": {
 | 
				
			||||||
    "modalTitle": "Informativna poruka",
 | 
					    "modalTitle": "Informativna poruka",
 | 
				
			||||||
 | 
				
			|||||||
@ -185,7 +185,7 @@
 | 
				
			|||||||
    "box_size_small": "小型 (顯示大約10行)",
 | 
					    "box_size_small": "小型 (顯示大約10行)",
 | 
				
			||||||
    "box_size_medium": "中型 (顯示大約30行)",
 | 
					    "box_size_medium": "中型 (顯示大約30行)",
 | 
				
			||||||
    "box_size_full": "完整顯示(完整文字框)",
 | 
					    "box_size_full": "完整顯示(完整文字框)",
 | 
				
			||||||
    "button_include": "包含筆記 <kbd>Enter</kbd>"
 | 
					    "button_include": "包含筆記"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "info": {
 | 
					  "info": {
 | 
				
			||||||
    "modalTitle": "資訊消息",
 | 
					    "modalTitle": "資訊消息",
 | 
				
			||||||
 | 
				
			|||||||
@ -1,116 +0,0 @@
 | 
				
			|||||||
import { t } from "../../services/i18n.js";
 | 
					 | 
				
			||||||
import treeService from "../../services/tree.js";
 | 
					 | 
				
			||||||
import noteAutocompleteService from "../../services/note_autocomplete.js";
 | 
					 | 
				
			||||||
import froca from "../../services/froca.js";
 | 
					 | 
				
			||||||
import BasicWidget from "../basic_widget.js";
 | 
					 | 
				
			||||||
import { Modal } from "bootstrap";
 | 
					 | 
				
			||||||
import type { EventData } from "../../components/app_context.js";
 | 
					 | 
				
			||||||
import type EditableTextTypeWidget from "../type_widgets/editable_text.js";
 | 
					 | 
				
			||||||
import { openDialog } from "../../services/dialog.js";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const TPL = /*html*/`
 | 
					 | 
				
			||||||
<div class="include-note-dialog modal mx-auto" tabindex="-1" role="dialog">
 | 
					 | 
				
			||||||
    <div class="modal-dialog modal-lg" role="document">
 | 
					 | 
				
			||||||
        <div class="modal-content">
 | 
					 | 
				
			||||||
            <div class="modal-header">
 | 
					 | 
				
			||||||
                <h5 class="modal-title">${t("include_note.dialog_title")}</h5>
 | 
					 | 
				
			||||||
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="${t("include_note.close")}"></button>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <form class="include-note-form">
 | 
					 | 
				
			||||||
                <div class="modal-body">
 | 
					 | 
				
			||||||
                    <div class="form-group">
 | 
					 | 
				
			||||||
                        <label for="include-note-autocomplete">${t("include_note.label_note")}</label>
 | 
					 | 
				
			||||||
                        <div class="input-group">
 | 
					 | 
				
			||||||
                            <input class="include-note-autocomplete form-control" placeholder="${t("include_note.placeholder_search")}">
 | 
					 | 
				
			||||||
                        </div>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    ${t("include_note.box_size_prompt")}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <div class="form-check">
 | 
					 | 
				
			||||||
                        <label class="form-check-label tn-radio">
 | 
					 | 
				
			||||||
                            <input class="form-check-input" type="radio" name="include-note-box-size" value="small">
 | 
					 | 
				
			||||||
                            ${t("include_note.box_size_small")}
 | 
					 | 
				
			||||||
                        </label>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                    <div class="form-check">
 | 
					 | 
				
			||||||
                        <label class="form-check-label tn-radio">
 | 
					 | 
				
			||||||
                            <input class="form-check-input" type="radio" name="include-note-box-size" value="medium" checked>
 | 
					 | 
				
			||||||
                            ${t("include_note.box_size_medium")}
 | 
					 | 
				
			||||||
                        </label>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                    <div class="form-check">
 | 
					 | 
				
			||||||
                        <label class="form-check-label tn-radio">
 | 
					 | 
				
			||||||
                            <input class="form-check-input" type="radio" name="include-note-box-size" value="full">
 | 
					 | 
				
			||||||
                            ${t("include_note.box_size_full")}
 | 
					 | 
				
			||||||
                        </label>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
                <div class="modal-footer">
 | 
					 | 
				
			||||||
                    <button type="submit" class="btn btn-primary">${t("include_note.button_include")}</button>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
            </form>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
</div>`;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default class IncludeNoteDialog extends BasicWidget {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private modal!: bootstrap.Modal;
 | 
					 | 
				
			||||||
    private $form!: JQuery<HTMLElement>;
 | 
					 | 
				
			||||||
    private $autoComplete!: JQuery<HTMLElement>;
 | 
					 | 
				
			||||||
    private textTypeWidget?: EditableTextTypeWidget;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    doRender() {
 | 
					 | 
				
			||||||
        this.$widget = $(TPL);
 | 
					 | 
				
			||||||
        this.modal = Modal.getOrCreateInstance(this.$widget[0]);
 | 
					 | 
				
			||||||
        this.$form = this.$widget.find(".include-note-form");
 | 
					 | 
				
			||||||
        this.$autoComplete = this.$widget.find(".include-note-autocomplete");
 | 
					 | 
				
			||||||
        this.$form.on("submit", () => {
 | 
					 | 
				
			||||||
            const notePath = this.$autoComplete.getSelectedNotePath();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (notePath) {
 | 
					 | 
				
			||||||
                this.modal.hide();
 | 
					 | 
				
			||||||
                this.includeNote(notePath);
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                logError("No noteId to include.");
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    async showIncludeNoteDialogEvent({ textTypeWidget }: EventData<"showIncludeDialog">) {
 | 
					 | 
				
			||||||
        this.textTypeWidget = textTypeWidget;
 | 
					 | 
				
			||||||
        await this.refresh();
 | 
					 | 
				
			||||||
        openDialog(this.$widget);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.$autoComplete.trigger("focus").trigger("select"); // to be able to quickly remove entered text
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    async refresh() {
 | 
					 | 
				
			||||||
        this.$autoComplete.val("");
 | 
					 | 
				
			||||||
        noteAutocompleteService.initNoteAutocomplete(this.$autoComplete, {
 | 
					 | 
				
			||||||
            hideGoToSelectedNoteButton: true,
 | 
					 | 
				
			||||||
            allowCreatingNotes: true
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
        noteAutocompleteService.showRecentNotes(this.$autoComplete);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    async includeNote(notePath: string) {
 | 
					 | 
				
			||||||
        const noteId = treeService.getNoteIdFromUrl(notePath);
 | 
					 | 
				
			||||||
        if (!noteId) {
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        const note = await froca.getNote(noteId);
 | 
					 | 
				
			||||||
        const boxSize = $("input[name='include-note-box-size']:checked").val() as string;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (["image", "canvas", "mermaid"].includes(note?.type ?? "")) {
 | 
					 | 
				
			||||||
            // there's no benefit to use insert note functionlity for images,
 | 
					 | 
				
			||||||
            // so we'll just add an IMG tag
 | 
					 | 
				
			||||||
            this.textTypeWidget?.addImage(noteId);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            this.textTypeWidget?.addIncludeNote(noteId, boxSize);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										99
									
								
								apps/client/src/widgets/dialogs/include_note.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								apps/client/src/widgets/dialogs/include_note.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,99 @@
 | 
				
			|||||||
 | 
					import { useRef, useState } from "preact/compat";
 | 
				
			||||||
 | 
					import type { EventData } from "../../components/app_context";
 | 
				
			||||||
 | 
					import { closeActiveDialog, openDialog } from "../../services/dialog";
 | 
				
			||||||
 | 
					import { t } from "../../services/i18n";
 | 
				
			||||||
 | 
					import FormGroup from "../react/FormGroup";
 | 
				
			||||||
 | 
					import FormRadioGroup from "../react/FormRadioGroup";
 | 
				
			||||||
 | 
					import Modal from "../react/Modal";
 | 
				
			||||||
 | 
					import NoteAutocomplete from "../react/NoteAutocomplete";
 | 
				
			||||||
 | 
					import ReactBasicWidget from "../react/ReactBasicWidget";
 | 
				
			||||||
 | 
					import Button from "../react/Button";
 | 
				
			||||||
 | 
					import note_autocomplete, { Suggestion } from "../../services/note_autocomplete";
 | 
				
			||||||
 | 
					import tree from "../../services/tree";
 | 
				
			||||||
 | 
					import froca from "../../services/froca";
 | 
				
			||||||
 | 
					import EditableTextTypeWidget from "../type_widgets/editable_text";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface IncludeNoteDialogProps {
 | 
				
			||||||
 | 
					    textTypeWidget?: EditableTextTypeWidget;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function IncludeNoteDialogComponent({ textTypeWidget }: IncludeNoteDialogProps) {
 | 
				
			||||||
 | 
					    const [suggestion, setSuggestion] = useState<Suggestion | null>(null);
 | 
				
			||||||
 | 
					    const [boxSize, setBoxSize] = useState("medium");
 | 
				
			||||||
 | 
					    const inputRef = useRef<HTMLInputElement>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (textTypeWidget &&
 | 
				
			||||||
 | 
					        <Modal
 | 
				
			||||||
 | 
					            className="include-note-dialog"
 | 
				
			||||||
 | 
					            title={t("include_note.dialog_title")}
 | 
				
			||||||
 | 
					            size="lg"
 | 
				
			||||||
 | 
					            onShown={() => {
 | 
				
			||||||
 | 
					                inputRef.current?.focus();
 | 
				
			||||||
 | 
					                note_autocomplete.showRecentNotes($(inputRef.current!));
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					            onSubmit={() => {
 | 
				
			||||||
 | 
					                if (!suggestion?.notePath) {
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                closeActiveDialog();
 | 
				
			||||||
 | 
					                includeNote(suggestion.notePath, textTypeWidget);
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					            footer={
 | 
				
			||||||
 | 
					                <Button text={t("include_note.button_include")} keyboardShortcut="Enter" />
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					            <FormGroup label={t("include_note.label_note")}>
 | 
				
			||||||
 | 
					                <NoteAutocomplete
 | 
				
			||||||
 | 
					                    placeholder={t("include_note.placeholder_search")}
 | 
				
			||||||
 | 
					                    onChange={setSuggestion}
 | 
				
			||||||
 | 
					                    inputRef={inputRef}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					            </FormGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <FormGroup label={t("include_note.box_size_prompt")}>
 | 
				
			||||||
 | 
					                <FormRadioGroup name="include-note-box-size"
 | 
				
			||||||
 | 
					                    currentValue={boxSize} onChange={setBoxSize}
 | 
				
			||||||
 | 
					                    values={[
 | 
				
			||||||
 | 
					                        { label: t("include_note.box_size_small"), value: "small" },
 | 
				
			||||||
 | 
					                        { label: t("include_note.box_size_medium"), value: "medium" },
 | 
				
			||||||
 | 
					                        { label: t("include_note.box_size_full"), value: "full" },
 | 
				
			||||||
 | 
					                    ]}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					            </FormGroup>
 | 
				
			||||||
 | 
					        </Modal>
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class IncludeNoteDialog extends ReactBasicWidget {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private props: IncludeNoteDialogProps = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    get component() {
 | 
				
			||||||
 | 
					        return <IncludeNoteDialogComponent {...this.props} />;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async showIncludeNoteDialogEvent({ textTypeWidget }: EventData<"showIncludeDialog">) {
 | 
				
			||||||
 | 
					        this.props = { textTypeWidget };
 | 
				
			||||||
 | 
					        this.doRender();
 | 
				
			||||||
 | 
					        openDialog(this.$widget);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function includeNote(notePath: string, textTypeWidget: EditableTextTypeWidget) {
 | 
				
			||||||
 | 
					    const noteId = tree.getNoteIdFromUrl(notePath);
 | 
				
			||||||
 | 
					    if (!noteId) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const note = await froca.getNote(noteId);
 | 
				
			||||||
 | 
					    const boxSize = $("input[name='include-note-box-size']:checked").val() as string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (["image", "canvas", "mermaid"].includes(note?.type ?? "")) {
 | 
				
			||||||
 | 
					        // there's no benefit to use insert note functionlity for images,
 | 
				
			||||||
 | 
					        // so we'll just add an IMG tag
 | 
				
			||||||
 | 
					        textTypeWidget.addImage(noteId);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        textTypeWidget.addIncludeNote(noteId, boxSize);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user