mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	feat(react/ribbon): display help tooltip in attribute editor
This commit is contained in:
		| @@ -16,12 +16,7 @@ import type { default as FAttribute, AttributeType } from "../../entities/fattri | ||||
| import type FNote from "../../entities/fnote.js"; | ||||
| import { escapeQuotes } from "../../services/utils.js"; | ||||
|  | ||||
| const HELP_TEXT = ` | ||||
| <p>${t("attribute_editor.help_text_body1")}</p> | ||||
|  | ||||
| <p>${t("attribute_editor.help_text_body2")}</p> | ||||
|  | ||||
| <p>${t("attribute_editor.help_text_body3")}</p>`; | ||||
|  | ||||
| const TPL = /*html*/` | ||||
|  | ||||
| @@ -229,9 +224,7 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget implem | ||||
|     } | ||||
|  | ||||
|     async handleEditorClick(e: JQuery.ClickEvent) { | ||||
|         const pos = this.textEditor.model.document.selection.getFirstPosition(); | ||||
|  | ||||
|         if (pos && pos.textNode && pos.textNode.data) { | ||||
|         if () { | ||||
|             const clickIndex = this.getClickIndex(pos); | ||||
|  | ||||
|             let parsedAttrs; | ||||
| @@ -267,25 +260,9 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget implem | ||||
|                     this.showHelpTooltip(); | ||||
|                 } | ||||
|             }, 100); | ||||
|         } else { | ||||
|             this.showHelpTooltip(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     showHelpTooltip() { | ||||
|         this.attributeDetailWidget.hide(); | ||||
|  | ||||
|         this.$editor.tooltip({ | ||||
|             trigger: "focus", | ||||
|             html: true, | ||||
|             title: HELP_TEXT, | ||||
|             placement: "bottom", | ||||
|             offset: "0,30" | ||||
|         }); | ||||
|  | ||||
|         this.$editor.tooltip("show"); | ||||
|     } | ||||
|  | ||||
|     getClickIndex(pos: ModelPosition) { | ||||
|         let clickIndex = pos.offset - (pos.textNode?.startOffset ?? 0); | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import type { AttributeEditor, EditorConfig } from "@triliumnext/ckeditor5"; | ||||
| import { CKTextEditor, type AttributeEditor, type EditorConfig, type ModelPosition } from "@triliumnext/ckeditor5"; | ||||
| import { useEffect, useRef } from "preact/compat"; | ||||
|  | ||||
| interface CKEditorOpts { | ||||
| @@ -9,15 +9,19 @@ interface CKEditorOpts { | ||||
|     disableNewlines?: boolean; | ||||
|     disableSpellcheck?: boolean; | ||||
|     onChange?: () => void; | ||||
|     onClick?: (pos?: ModelPosition | null) => void; | ||||
| } | ||||
|  | ||||
| export default function CKEditor({ className, tabIndex, editor, config, disableNewlines, disableSpellcheck, onChange }: CKEditorOpts) { | ||||
| export default function CKEditor({ className, tabIndex, editor, config, disableNewlines, disableSpellcheck, onChange, onClick }: CKEditorOpts) { | ||||
|     const editorContainerRef = useRef<HTMLDivElement>(null);     | ||||
|     const textEditorRef = useRef<CKTextEditor>(null); | ||||
|  | ||||
|     useEffect(() => { | ||||
|         if (!editorContainerRef.current) return; | ||||
|  | ||||
|         editor.create(editorContainerRef.current, config).then((textEditor) => { | ||||
|             textEditorRef.current = textEditor; | ||||
|  | ||||
|             if (disableNewlines) { | ||||
|                 textEditor.editing.view.document.on( | ||||
|                     "enter", | ||||
| @@ -48,6 +52,12 @@ export default function CKEditor({ className, tabIndex, editor, config, disableN | ||||
|             ref={editorContainerRef} | ||||
|             className={className} | ||||
|             tabIndex={tabIndex} | ||||
|             onClick={() => { | ||||
|                 if (onClick) { | ||||
|                     const pos = textEditorRef.current?.model.document.selection.getFirstPosition(); | ||||
|                     onClick(pos); | ||||
|                 } | ||||
|             }} | ||||
|         /> | ||||
|     ) | ||||
| } | ||||
| @@ -12,7 +12,8 @@ import FNote from "../../entities/fnote"; | ||||
| import attributes from "../../services/attributes"; | ||||
| import FBlob from "../../entities/fblob"; | ||||
| import NoteContextAwareWidget from "../note_context_aware_widget"; | ||||
| import { RefObject, VNode } from "preact"; | ||||
| import { Ref, RefObject, VNode } from "preact"; | ||||
| import { Tooltip } from "bootstrap"; | ||||
|  | ||||
| type TriliumEventHandler<T extends EventNames> = (data: EventData<T>) => void; | ||||
| const registeredHandlers: Map<Component, Map<EventNames, TriliumEventHandler<any>[]>> = new Map(); | ||||
| @@ -511,3 +512,28 @@ export function useWindowSize() { | ||||
|  | ||||
|     return size; | ||||
| } | ||||
|  | ||||
| export function useTooltip(elRef: RefObject<HTMLElement>, config: Partial<Tooltip.Options>) { | ||||
|     useEffect(() => { | ||||
|         if (!elRef?.current) return; | ||||
|  | ||||
|         const $el = $(elRef.current); | ||||
|         $el.tooltip(config);         | ||||
|     }, [ elRef, config ]); | ||||
|  | ||||
|     const showTooltip = useCallback(() => { | ||||
|         if (!elRef?.current) return; | ||||
|  | ||||
|         const $el = $(elRef.current); | ||||
|         $el.tooltip("show"); | ||||
|     }, [ elRef ]); | ||||
|  | ||||
|     const hideTooltip = useCallback(() => { | ||||
|         if (!elRef?.current) return; | ||||
|  | ||||
|         const $el = $(elRef.current); | ||||
|         $el.tooltip("hide"); | ||||
|     }, [ elRef ]); | ||||
|  | ||||
|     return { showTooltip, hideTooltip }; | ||||
| } | ||||
| @@ -1,9 +1,17 @@ | ||||
| import { useCallback, useEffect, useMemo, useRef, useState } from "preact/hooks" | ||||
| import { AttributeEditor as CKEditorAttributeEditor, EditorConfig, MentionFeed } from "@triliumnext/ckeditor5"; | ||||
| import { useEffect, useRef, useState } from "preact/hooks" | ||||
| import { AttributeEditor as CKEditorAttributeEditor, MentionFeed } from "@triliumnext/ckeditor5"; | ||||
| import { t } from "../../../services/i18n"; | ||||
| import server from "../../../services/server"; | ||||
| import note_autocomplete, { Suggestion } from "../../../services/note_autocomplete"; | ||||
| import CKEditor from "../../react/CKEditor"; | ||||
| import { useTooltip } from "../../react/hooks"; | ||||
|  | ||||
| const HELP_TEXT = ` | ||||
| <p>${t("attribute_editor.help_text_body1")}</p> | ||||
|  | ||||
| <p>${t("attribute_editor.help_text_body2")}</p> | ||||
|  | ||||
| <p>${t("attribute_editor.help_text_body3")}</p>`; | ||||
|  | ||||
| const mentionSetup: MentionFeed[] = [ | ||||
|     { | ||||
| @@ -52,10 +60,26 @@ const mentionSetup: MentionFeed[] = [ | ||||
|  | ||||
| export default function AttributeEditor() { | ||||
|  | ||||
|     const [ attributeDetailVisible, setAttributeDetailVisible ] = useState(false); | ||||
|     const [ state, setState ] = useState<"normal" | "showHelpTooltip" | "showAttributeDetail">(); | ||||
|     const wrapperRef = useRef<HTMLDivElement>(null); | ||||
|     const { showTooltip, hideTooltip } = useTooltip(wrapperRef, { | ||||
|         trigger: "focus", | ||||
|         html: true, | ||||
|         title: HELP_TEXT, | ||||
|         placement: "bottom", | ||||
|         offset: "0,30" | ||||
|     }); | ||||
|  | ||||
|     useEffect(() => { | ||||
|         if (state === "showHelpTooltip") { | ||||
|             showTooltip(); | ||||
|         } else { | ||||
|             hideTooltip(); | ||||
|         } | ||||
|     }, [ state ]); | ||||
|      | ||||
|     return ( | ||||
|         <div style="position: relative; padding-top: 10px; padding-bottom: 10px"> | ||||
|         <div ref={wrapperRef} style="position: relative; padding-top: 10px; padding-bottom: 10px"> | ||||
|             <CKEditor | ||||
|                 className="attribute-list-editor" | ||||
|                 tabIndex={200} | ||||
| @@ -69,6 +93,13 @@ export default function AttributeEditor() { | ||||
|                 onChange={() => { | ||||
|                     console.log("Data changed!"); | ||||
|                 }} | ||||
|                 onClick={(pos) => { | ||||
|                     if (pos && pos.textNode && pos.textNode.data) { | ||||
|                         setState("showAttributeDetail") | ||||
|                     } else { | ||||
|                         setState("showHelpTooltip"); | ||||
|                     } | ||||
|                 }} | ||||
|                 disableNewlines disableSpellcheck | ||||
|             /> | ||||
|         </div> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user