diff --git a/apps/client/src/widgets/react/FormList.tsx b/apps/client/src/widgets/react/FormList.tsx index dd40bd08c..84cc4d8c6 100644 --- a/apps/client/src/widgets/react/FormList.tsx +++ b/apps/client/src/widgets/react/FormList.tsx @@ -2,13 +2,13 @@ import "./FormList.css"; import { Dropdown as BootstrapDropdown, Tooltip } from "bootstrap"; import clsx from "clsx"; -import { ComponentChildren } from "preact"; +import { ComponentChildren, RefObject } from "preact"; import { type CSSProperties,useEffect, useMemo, useRef, useState } from "preact/compat"; import { CommandNames } from "../../components/app_context"; import { handleRightToLeftPlacement, isMobile, openInAppHelpFromUrl } from "../../services/utils"; import FormToggle from "./FormToggle"; -import { useStaticTooltip } from "./hooks"; +import { useStaticTooltip, useSyncedRef } from "./hooks"; import Icon from "./Icon"; interface FormListOpts { @@ -97,6 +97,7 @@ interface FormListItemOpts { className?: string; rtl?: boolean; postContent?: ComponentChildren; + itemRef?: RefObject; } const TOOLTIP_CONFIG: Partial = { @@ -104,8 +105,8 @@ const TOOLTIP_CONFIG: Partial = { fallbackPlacements: [ handleRightToLeftPlacement("right") ] }; -export function FormListItem({ className, icon, value, title, active, disabled, checked, container, onClick, selected, rtl, triggerCommand, description, ...contentProps }: FormListItemOpts) { - const itemRef = useRef(null); +export function FormListItem({ className, icon, value, title, active, disabled, checked, container, onClick, selected, rtl, triggerCommand, description, itemRef: externalItemRef, ...contentProps }: FormListItemOpts) { + const itemRef = useSyncedRef(externalItemRef, null); if (checked) { icon = "bx bx-check"; diff --git a/apps/client/src/widgets/ribbon/NoteActions.tsx b/apps/client/src/widgets/ribbon/NoteActions.tsx index a6aaf14ff..dfb0d93bb 100644 --- a/apps/client/src/widgets/ribbon/NoteActions.tsx +++ b/apps/client/src/widgets/ribbon/NoteActions.tsx @@ -1,6 +1,7 @@ import { ConvertToAttachmentResponse } from "@triliumnext/commons"; import { Dropdown as BootstrapDropdown } from "bootstrap"; -import { useContext, useRef } from "preact/hooks"; +import { RefObject } from "preact"; +import { useContext, useEffect, useRef } from "preact/hooks"; import appContext, { CommandNames } from "../../components/app_context"; import Component from "../../components/component"; @@ -60,6 +61,8 @@ function RevisionsButton({ note }: { note: FNote }) { ); } +type ItemToFocus = "basic-properties"; + function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: NoteContext }) { const dropdownRef = useRef(null); const parentComponent = useContext(ParentComponent); @@ -79,10 +82,12 @@ function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: Not const [syncServerHost] = useTriliumOption("syncServerHost"); const { isReadOnly, enableEditing } = useIsNoteReadOnly(note, noteContext); const isNormalViewMode = noteContext?.viewScope?.viewMode === "default"; + const itemToFocusRef = useRef(null); // Keyboard shortcuts. useTriliumEvent("toggleRibbonTabBasicProperties", () => { if (!isNewLayout) return; + itemToFocusRef.current = "basic-properties"; dropdownRef.current?.toggle(); }); @@ -93,7 +98,9 @@ function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: Not className="note-actions" hideToggleArrow noSelectButtonStyle - iconAction> + iconAction + onHidden={() => itemToFocusRef.current = null } + > {isReadOnly && <> {isNewLayout && isNormalViewMode && !isHelpPage && <> - + } @@ -157,14 +164,22 @@ function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: Not ); } -function NoteBasicProperties({ note }: { +function NoteBasicProperties({ note, focus }: { note: FNote; + focus: RefObject; }) { + const itemToFocusRef = useRef(null); const [ isBookmarked, setIsBookmarked ] = useNoteBookmarkState(note); const [ isShared, switchShareState ] = useShareState(note); const [ isTemplate, setIsTemplate ] = useNoteLabelBoolean(note, "template"); const isProtected = useNoteProperty(note, "isProtected"); + useEffect(() => { + if (focus.current === "basic-properties") { + itemToFocusRef.current?.focus(); + } + }, [ focus ]); + return <>