feat(layout): focus exactly on basic properties

This commit is contained in:
Elian Doran 2025-12-16 18:29:46 +02:00
parent c0eb34927f
commit 713900b2b3
No known key found for this signature in database
2 changed files with 25 additions and 8 deletions

View File

@ -2,13 +2,13 @@ import "./FormList.css";
import { Dropdown as BootstrapDropdown, Tooltip } from "bootstrap"; import { Dropdown as BootstrapDropdown, Tooltip } from "bootstrap";
import clsx from "clsx"; import clsx from "clsx";
import { ComponentChildren } from "preact"; import { ComponentChildren, RefObject } from "preact";
import { type CSSProperties,useEffect, useMemo, useRef, useState } from "preact/compat"; import { type CSSProperties,useEffect, useMemo, useRef, useState } from "preact/compat";
import { CommandNames } from "../../components/app_context"; import { CommandNames } from "../../components/app_context";
import { handleRightToLeftPlacement, isMobile, openInAppHelpFromUrl } from "../../services/utils"; import { handleRightToLeftPlacement, isMobile, openInAppHelpFromUrl } from "../../services/utils";
import FormToggle from "./FormToggle"; import FormToggle from "./FormToggle";
import { useStaticTooltip } from "./hooks"; import { useStaticTooltip, useSyncedRef } from "./hooks";
import Icon from "./Icon"; import Icon from "./Icon";
interface FormListOpts { interface FormListOpts {
@ -97,6 +97,7 @@ interface FormListItemOpts {
className?: string; className?: string;
rtl?: boolean; rtl?: boolean;
postContent?: ComponentChildren; postContent?: ComponentChildren;
itemRef?: RefObject<HTMLLIElement>;
} }
const TOOLTIP_CONFIG: Partial<Tooltip.Options> = { const TOOLTIP_CONFIG: Partial<Tooltip.Options> = {
@ -104,8 +105,8 @@ const TOOLTIP_CONFIG: Partial<Tooltip.Options> = {
fallbackPlacements: [ handleRightToLeftPlacement("right") ] fallbackPlacements: [ handleRightToLeftPlacement("right") ]
}; };
export function FormListItem({ className, icon, value, title, active, disabled, checked, container, onClick, selected, rtl, triggerCommand, description, ...contentProps }: FormListItemOpts) { export function FormListItem({ className, icon, value, title, active, disabled, checked, container, onClick, selected, rtl, triggerCommand, description, itemRef: externalItemRef, ...contentProps }: FormListItemOpts) {
const itemRef = useRef<HTMLLIElement>(null); const itemRef = useSyncedRef<HTMLLIElement>(externalItemRef, null);
if (checked) { if (checked) {
icon = "bx bx-check"; icon = "bx bx-check";

View File

@ -1,6 +1,7 @@
import { ConvertToAttachmentResponse } from "@triliumnext/commons"; import { ConvertToAttachmentResponse } from "@triliumnext/commons";
import { Dropdown as BootstrapDropdown } from "bootstrap"; 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 appContext, { CommandNames } from "../../components/app_context";
import Component from "../../components/component"; 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 }) { function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: NoteContext }) {
const dropdownRef = useRef<BootstrapDropdown>(null); const dropdownRef = useRef<BootstrapDropdown>(null);
const parentComponent = useContext(ParentComponent); const parentComponent = useContext(ParentComponent);
@ -79,10 +82,12 @@ function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: Not
const [syncServerHost] = useTriliumOption("syncServerHost"); const [syncServerHost] = useTriliumOption("syncServerHost");
const { isReadOnly, enableEditing } = useIsNoteReadOnly(note, noteContext); const { isReadOnly, enableEditing } = useIsNoteReadOnly(note, noteContext);
const isNormalViewMode = noteContext?.viewScope?.viewMode === "default"; const isNormalViewMode = noteContext?.viewScope?.viewMode === "default";
const itemToFocusRef = useRef<ItemToFocus>(null);
// Keyboard shortcuts. // Keyboard shortcuts.
useTriliumEvent("toggleRibbonTabBasicProperties", () => { useTriliumEvent("toggleRibbonTabBasicProperties", () => {
if (!isNewLayout) return; if (!isNewLayout) return;
itemToFocusRef.current = "basic-properties";
dropdownRef.current?.toggle(); dropdownRef.current?.toggle();
}); });
@ -93,7 +98,9 @@ function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: Not
className="note-actions" className="note-actions"
hideToggleArrow hideToggleArrow
noSelectButtonStyle noSelectButtonStyle
iconAction> iconAction
onHidden={() => itemToFocusRef.current = null }
>
{isReadOnly && <> {isReadOnly && <>
<CommandItem icon="bx bx-pencil" text={t("read-only-info.edit-note")} <CommandItem icon="bx bx-pencil" text={t("read-only-info.edit-note")}
@ -108,7 +115,7 @@ function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: Not
<FormDropdownDivider /> <FormDropdownDivider />
{isNewLayout && isNormalViewMode && !isHelpPage && <> {isNewLayout && isNormalViewMode && !isHelpPage && <>
<NoteBasicProperties note={note} /> <NoteBasicProperties note={note} focus={itemToFocusRef} />
<FormDropdownDivider /> <FormDropdownDivider />
</>} </>}
@ -157,14 +164,22 @@ function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: Not
); );
} }
function NoteBasicProperties({ note }: { function NoteBasicProperties({ note, focus }: {
note: FNote; note: FNote;
focus: RefObject<ItemToFocus>;
}) { }) {
const itemToFocusRef = useRef<HTMLLIElement>(null);
const [ isBookmarked, setIsBookmarked ] = useNoteBookmarkState(note); const [ isBookmarked, setIsBookmarked ] = useNoteBookmarkState(note);
const [ isShared, switchShareState ] = useShareState(note); const [ isShared, switchShareState ] = useShareState(note);
const [ isTemplate, setIsTemplate ] = useNoteLabelBoolean(note, "template"); const [ isTemplate, setIsTemplate ] = useNoteLabelBoolean(note, "template");
const isProtected = useNoteProperty(note, "isProtected"); const isProtected = useNoteProperty(note, "isProtected");
useEffect(() => {
if (focus.current === "basic-properties") {
itemToFocusRef.current?.focus();
}
}, [ focus ]);
return <> return <>
<FormListToggleableItem <FormListToggleableItem
icon="bx bx-share-alt" icon="bx bx-share-alt"
@ -172,6 +187,7 @@ function NoteBasicProperties({ note }: {
currentValue={isShared} onChange={switchShareState} currentValue={isShared} onChange={switchShareState}
helpPage="R9pX4DGra2Vt" helpPage="R9pX4DGra2Vt"
disabled={["root", "_share", "_hidden"].includes(note?.noteId ?? "") || note?.noteId.startsWith("_options")} disabled={["root", "_share", "_hidden"].includes(note?.noteId ?? "") || note?.noteId.startsWith("_options")}
itemRef={itemToFocusRef}
/> />
<FormListToggleableItem <FormListToggleableItem
icon="bx bx-lock-alt" icon="bx bx-lock-alt"