({
- x: e.pageX,
- y: e.pageY,
- items,
- selectMenuItemHandler: async ({ command }) => {
- if (command === "insertChildNote") {
- note_create.createNote(appContext.tabManager.getActiveContextNotePath() ?? undefined);
- } else if (command === "delete") {
- const notePath = appContext.tabManager.getActiveContextNotePath();
- if (!notePath) {
- throw new Error("Cannot get note path to delete.");
- }
-
- const branchId = await tree.getBranchIdFromUrl(notePath);
-
- if (!branchId) {
- throw new Error(t("mobile_detail_menu.error_cannot_get_branch_id", { notePath }));
- }
-
- if (await branches.deleteNotes([branchId]) && parentComponent) {
- parentComponent.triggerCommand("setActiveScreen", { screen: "tree" });
- }
- } else if (command && parentComponent) {
- parentComponent.triggerCommand(command, { ntxId });
- }
- },
- forcePositionOnMobile: true
- });
- }}
- />
+
+ {note && (
+
+ noteContext?.notePath && note_create.createNote(noteContext.notePath)}
+ icon="bx bx-plus"
+ >{t("mobile_detail_menu.insert_child_note")}
+ {helpUrl && <>
+
+ openInAppHelpFromUrl(helpUrl)}
+ >{t("help-button.title")}
+ >}
+ {subContexts.length < 2 && <>
+
+ parentComponent.triggerCommand("openNewNoteSplit", { ntxId })}
+ icon="bx bx-dock-right"
+ >{t("create_pane_button.create_new_split")}
+ >}
+ {!isMainContext && <>
+
+ {
+ // Wait first for the context menu to be dismissed, otherwise the backdrop stays on.
+ requestAnimationFrame(() => {
+ parentComponent.triggerCommand("closeThisNoteSplit", { ntxId });
+ });
+ }}
+ >{t("close_pane_button.close_this_pane")}
+ >}
+
+ >}
+ />
+ )}
+
);
}
diff --git a/apps/client/src/widgets/react/Dropdown.tsx b/apps/client/src/widgets/react/Dropdown.tsx
index 5af2f6228..407b14a63 100644
--- a/apps/client/src/widgets/react/Dropdown.tsx
+++ b/apps/client/src/widgets/react/Dropdown.tsx
@@ -3,6 +3,7 @@ import { ComponentChildren, HTMLAttributes } from "preact";
import { CSSProperties, HTMLProps } from "preact/compat";
import { MutableRef, useCallback, useEffect, useRef, useState } from "preact/hooks";
+import { isMobile } from "../../services/utils";
import { useTooltip, useUniqueName } from "./hooks";
type DataAttributes = {
@@ -32,9 +33,10 @@ export interface DropdownProps extends Pick, "id" | "c
dropdownRef?: MutableRef;
titlePosition?: "top" | "right" | "bottom" | "left";
titleOptions?: Partial;
+ mobileBackdrop?: boolean;
}
-export default function Dropdown({ id, className, buttonClassName, isStatic, children, title, text, dropdownContainerStyle, dropdownContainerClassName, dropdownContainerRef: externalContainerRef, hideToggleArrow, iconAction, disabled, noSelectButtonStyle, noDropdownListStyle, forceShown, onShown: externalOnShown, onHidden: externalOnHidden, dropdownOptions, buttonProps, dropdownRef, titlePosition, titleOptions }: DropdownProps) {
+export default function Dropdown({ id, className, buttonClassName, isStatic, children, title, text, dropdownContainerStyle, dropdownContainerClassName, dropdownContainerRef: externalContainerRef, hideToggleArrow, iconAction, disabled, noSelectButtonStyle, noDropdownListStyle, forceShown, onShown: externalOnShown, onHidden: externalOnHidden, dropdownOptions, buttonProps, dropdownRef, titlePosition, titleOptions, mobileBackdrop }: DropdownProps) {
const containerRef = useRef(null);
const triggerRef = useRef(null);
const dropdownContainerRef = useRef(null);
@@ -74,12 +76,18 @@ export default function Dropdown({ id, className, buttonClassName, isStatic, chi
setShown(true);
externalOnShown?.();
hideTooltip();
- }, [ hideTooltip ]);
+ if (mobileBackdrop && isMobile()) {
+ document.getElementById("context-menu-cover")?.classList.add("show", "global-menu-cover");
+ }
+ }, [ hideTooltip, mobileBackdrop ]);
const onHidden = useCallback(() => {
setShown(false);
externalOnHidden?.();
- }, []);
+ if (mobileBackdrop && isMobile()) {
+ document.getElementById("context-menu-cover")?.classList.remove("show", "global-menu-cover");
+ }
+ }, [ mobileBackdrop ]);
useEffect(() => {
if (!containerRef.current) return;
diff --git a/apps/client/src/widgets/ribbon/NoteActions.tsx b/apps/client/src/widgets/ribbon/NoteActions.tsx
index 6db1d384e..51788085d 100644
--- a/apps/client/src/widgets/ribbon/NoteActions.tsx
+++ b/apps/client/src/widgets/ribbon/NoteActions.tsx
@@ -1,6 +1,6 @@
import { ConvertToAttachmentResponse } from "@triliumnext/commons";
import { Dropdown as BootstrapDropdown } from "bootstrap";
-import { RefObject } from "preact";
+import { ComponentChildren, RefObject } from "preact";
import { useContext, useEffect, useRef } from "preact/hooks";
import appContext, { CommandNames } from "../../components/app_context";
@@ -63,7 +63,7 @@ function RevisionsButton({ note }: { note: FNote }) {
type ItemToFocus = "basic-properties";
-function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: NoteContext }) {
+export function NoteContextMenu({ note, noteContext, extraItems }: { note: FNote, noteContext?: NoteContext, extraItems?: ComponentChildren; }) {
const dropdownRef = useRef(null);
const parentComponent = useContext(ParentComponent);
const noteType = useNoteProperty(note, "type") ?? "";
@@ -99,12 +99,15 @@ function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: Not
dropdownRef={dropdownRef}
buttonClassName={ isNewLayout ? "bx bx-dots-horizontal-rounded" : "bx bx-dots-vertical-rounded" }
className="note-actions"
+ dropdownContainerClassName="mobile-bottom-menu"
hideToggleArrow
noSelectButtonStyle
noDropdownListStyle
iconAction
onHidden={() => itemToFocusRef.current = null }
+ mobileBackdrop
>
+ {extraItems}
{isReadOnly && <>
void), disabled?: boolean, destructive?: boolean }) {
+export function CommandItem({ icon, text, title, command, disabled }: { icon: string, text: string, title?: string, command: CommandNames | (() => void), disabled?: boolean, destructive?: boolean }) {
return