mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	feat(react/dialogs): port include_note
This commit is contained in:
		| @@ -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); | ||||||
|  |     } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user