chore(react/ribbon): add focus to attribute editor

This commit is contained in:
Elian Doran
2025-08-23 20:31:00 +03:00
parent efd713dc61
commit db687197de
5 changed files with 46 additions and 30 deletions

View File

@@ -18,14 +18,6 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget implem
}
}
async save() {
if (this.lastUpdatedNoteId !== this.noteId) {
// https://github.com/zadam/trilium/issues/3090
console.warn("Ignoring blur event because a different note is loaded.");
return;
}
}
dataChanged() {
this.lastUpdatedNoteId = this.noteId;
}
@@ -49,20 +41,6 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget implem
focus() {
this.$editor.trigger("focus");
this.textEditor.model.change((writer) => {
const documentRoot = this.textEditor.editing.model.document.getRoot();
if (!documentRoot) {
return;
}
const positionAt = writer.createPositionAt(documentRoot, "end");
writer.setSelection(positionAt);
});
}
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
if (loadResults.getAttributeRows(this.componentId).find((attr) => attributeService.isAffecting(attr, this.note))) {
this.refresh();
}
}
}

View File

@@ -1,7 +1,13 @@
import { CKTextEditor, type AttributeEditor, type EditorConfig, type ModelPosition } from "@triliumnext/ckeditor5";
import { useEffect, useRef } from "preact/compat";
import { useEffect, useImperativeHandle, useRef } from "preact/compat";
import { MutableRef } from "preact/hooks";
export interface CKEditorApi {
focus: () => void;
}
interface CKEditorOpts {
apiRef: MutableRef<CKEditorApi | undefined>;
currentValue?: string;
className: string;
tabIndex?: number;
@@ -15,9 +21,22 @@ interface CKEditorOpts {
onBlur?: () => void;
}
export default function CKEditor({ currentValue, editor, config, disableNewlines, disableSpellcheck, onChange, onClick, ...restProps }: CKEditorOpts) {
export default function CKEditor({ apiRef, currentValue, editor, config, disableNewlines, disableSpellcheck, onChange, onClick, ...restProps }: CKEditorOpts) {
const editorContainerRef = useRef<HTMLDivElement>(null);
const textEditorRef = useRef<CKTextEditor>(null);
useImperativeHandle(apiRef, () => {
return {
focus() {
editorContainerRef.current?.focus();
textEditorRef.current?.model.change((writer) => {
const documentRoot = textEditorRef.current?.editing.model.document.getRoot();
if (documentRoot) {
writer.setSelection(writer.createPositionAt(documentRoot, "end"));
}
});
}
};
}, [ editorContainerRef ]);
useEffect(() => {
if (!editorContainerRef.current) return;

View File

@@ -148,7 +148,7 @@ const TAB_CONFIGURATION = numberObjectsInPlace<TabConfiguration>([
]);
export default function Ribbon() {
const { note, ntxId, hoistedNoteId, notePath, noteContext } = useNoteContext();
const { note, ntxId, hoistedNoteId, notePath, noteContext, componentId } = useNoteContext();
const titleContext: TitleContext = { note };
const [ activeTabIndex, setActiveTabIndex ] = useState<number | undefined>();
const filteredTabs = useMemo(() => TAB_CONFIGURATION.filter(tab => typeof tab.show === "boolean" ? tab.show : tab.show?.(titleContext)), [ titleContext, note ]);
@@ -190,7 +190,8 @@ export default function Ribbon() {
ntxId,
hoistedNoteId,
notePath,
noteContext
noteContext,
componentId
});
})}
</div>

View File

@@ -3,8 +3,8 @@ import { AttributeEditor as CKEditorAttributeEditor, MentionFeed, ModelElement,
import { t } from "../../../services/i18n";
import server from "../../../services/server";
import note_autocomplete, { Suggestion } from "../../../services/note_autocomplete";
import CKEditor from "../../react/CKEditor";
import { useLegacyWidget, useTooltip } from "../../react/hooks";
import CKEditor, { CKEditorApi } from "../../react/CKEditor";
import { useLegacyWidget, useTooltip, useTriliumEventBeta } from "../../react/hooks";
import FAttribute from "../../../entities/fattribute";
import attribute_renderer from "../../../services/attribute_renderer";
import FNote from "../../../entities/fnote";
@@ -19,6 +19,7 @@ import froca from "../../../services/froca";
import contextMenu from "../../../menus/context_menu";
import type { CommandData, FilteredCommandNames } from "../../../components/app_context";
import { AttributeType } from "@triliumnext/commons";
import attributes from "../../../services/attributes";
type AttributeCommandNames = FilteredCommandNames<CommandData>;
@@ -86,6 +87,7 @@ export default function AttributeEditor({ note, componentId }: { note: FNote, co
const lastSavedContent = useRef<string>();
const currentValueRef = useRef(initialValue);
const wrapperRef = useRef<HTMLDivElement>(null);
const editorRef = useRef<CKEditorApi>();
const { showTooltip, hideTooltip } = useTooltip(wrapperRef, {
trigger: "focus",
@@ -207,9 +209,23 @@ export default function AttributeEditor({ note, componentId }: { note: FNote, co
}, 100);
}
useEffect(() => {
// Refresh with note
function refresh() {
renderOwnedAttributes(note.getOwnedAttributes(), true);
}, [ note ]);
}
useEffect(() => refresh(), [ note ]);
useTriliumEventBeta("entitiesReloaded", ({ loadResults }) => {
if (loadResults.getAttributeRows(componentId).find((attr) => attributes.isAffecting(attr, note))) {
console.log("Trigger due to entities reloaded");
refresh();
}
});
// Focus on show.
useEffect(() => {
setTimeout(() => editorRef.current?.focus(), 0);
}, []);
return (
<>
@@ -224,6 +240,7 @@ export default function AttributeEditor({ note, componentId }: { note: FNote, co
}}
>
<CKEditor
apiRef={editorRef}
className="attribute-list-editor"
tabIndex={200}
editor={CKEditorAttributeEditor}

View File

@@ -8,4 +8,5 @@ export interface TabContext {
hoistedNoteId?: string;
notePath?: string | null;
noteContext?: NoteContext;
componentId: string;
}