diff --git a/apps/client/src/layouts/desktop_layout.tsx b/apps/client/src/layouts/desktop_layout.tsx index cebdf423d..4c642495a 100644 --- a/apps/client/src/layouts/desktop_layout.tsx +++ b/apps/client/src/layouts/desktop_layout.tsx @@ -47,6 +47,7 @@ import LauncherContainer from "../widgets/launch_bar/LauncherContainer.jsx"; import Breadcrumb from "../widgets/Breadcrumb.jsx"; import TabHistoryNavigationButtons from "../widgets/TabHistoryNavigationButtons.jsx"; import { experimentalFeatures, isExperimentalFeatureEnabled } from "../services/experimental_features.js"; +import NoteActions from "../widgets/ribbon/NoteActions.jsx"; export default class DesktopLayout { @@ -139,13 +140,14 @@ export default class DesktopLayout { .child() .child() .child() + .optChild(isNewLayout, ) ) .child(new FlexContainer("row") .class("title-row") .child() .child() ) - .optChild(!isNewLayout, ) + .optChild(!isNewLayout, ) .child(new WatchedFileUpdateStatusWidget()) .child() .child( diff --git a/apps/client/src/widgets/ribbon/NoteActions.tsx b/apps/client/src/widgets/ribbon/NoteActions.tsx index f1af3b20b..be2b79251 100644 --- a/apps/client/src/widgets/ribbon/NoteActions.tsx +++ b/apps/client/src/widgets/ribbon/NoteActions.tsx @@ -1,115 +1,111 @@ import { ConvertToAttachmentResponse } from "@triliumnext/commons"; -import { FormDropdownDivider, FormListHeader, FormListItem } from "../react/FormList"; -import { isElectron as getIsElectron, isMac as getIsMac } from "../../services/utils"; -import { ParentComponent } from "../react/react_utils"; -import { t } from "../../services/i18n" import { useContext } from "preact/hooks"; -import { useIsNoteReadOnly, useNoteLabel, useNoteProperty } from "../react/hooks"; -import { useTriliumOption } from "../react/hooks"; -import ActionButton from "../react/ActionButton" + import appContext, { CommandNames } from "../../components/app_context"; +import NoteContext from "../../components/note_context"; +import FNote from "../../entities/fnote"; import branches from "../../services/branches"; import dialog from "../../services/dialog"; -import Dropdown from "../react/Dropdown"; -import FNote from "../../entities/fnote" -import NoteContext from "../../components/note_context"; +import { t } from "../../services/i18n"; import server from "../../services/server"; import toast from "../../services/toast"; +import { isElectron as getIsElectron, isMac as getIsMac } from "../../services/utils"; import ws from "../../services/ws"; +import ActionButton from "../react/ActionButton"; +import Dropdown from "../react/Dropdown"; +import { FormDropdownDivider, FormListHeader, FormListItem } from "../react/FormList"; +import { useIsNoteReadOnly, useNoteContext, useNoteLabel, useNoteProperty, useTriliumOption } from "../react/hooks"; +import { ParentComponent } from "../react/react_utils"; -interface NoteActionsProps { - note?: FNote; - noteContext?: NoteContext; -} - -export default function NoteActions({ note, noteContext }: NoteActionsProps) { - return ( - <> - {note && } - {note && note.type !== "launcher" && } - - ); +export default function NoteActions() { + const { note, noteContext } = useNoteContext(); + return ( +
+ {note && } + {note && note.type !== "launcher" && } +
+ ); } function RevisionsButton({ note }: { note: FNote }) { - const isEnabled = !["launcher", "doc"].includes(note?.type ?? ""); + const isEnabled = !["launcher", "doc"].includes(note?.type ?? ""); - return (isEnabled && - - ); + return (isEnabled && + + ); } function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: NoteContext }) { - const parentComponent = useContext(ParentComponent); - const noteType = useNoteProperty(note, "type") ?? ""; - const [ viewType ] = useNoteLabel(note, "viewType"); - const canBeConvertedToAttachment = note?.isEligibleForConversionToAttachment(); - const isSearchable = ["text", "code", "book", "mindMap", "doc"].includes(noteType); - const isInOptionsOrHelp = note?.noteId.startsWith("_options") || note?.noteId.startsWith("_help"); - const isPrintable = ["text", "code"].includes(noteType) || (noteType === "book" && ["presentation", "list", "table"].includes(viewType ?? "")); - const isElectron = getIsElectron(); - const isMac = getIsMac(); - const hasSource = ["text", "code", "relationMap", "mermaid", "canvas", "mindMap", "aiChat"].includes(noteType); - const isSearchOrBook = ["search", "book"].includes(noteType); - const [ syncServerHost ] = useTriliumOption("syncServerHost"); - const {isReadOnly, enableEditing} = useIsNoteReadOnly(note, noteContext); + const parentComponent = useContext(ParentComponent); + const noteType = useNoteProperty(note, "type") ?? ""; + const [viewType] = useNoteLabel(note, "viewType"); + const canBeConvertedToAttachment = note?.isEligibleForConversionToAttachment(); + const isSearchable = ["text", "code", "book", "mindMap", "doc"].includes(noteType); + const isInOptionsOrHelp = note?.noteId.startsWith("_options") || note?.noteId.startsWith("_help"); + const isPrintable = ["text", "code"].includes(noteType) || (noteType === "book" && ["presentation", "list", "table"].includes(viewType ?? "")); + const isElectron = getIsElectron(); + const isMac = getIsMac(); + const hasSource = ["text", "code", "relationMap", "mermaid", "canvas", "mindMap", "aiChat"].includes(noteType); + const isSearchOrBook = ["search", "book"].includes(noteType); + const [syncServerHost] = useTriliumOption("syncServerHost"); + const { isReadOnly, enableEditing } = useIsNoteReadOnly(note, noteContext); - return ( - + return ( + - {isReadOnly && <> - enableEditing()} /> - - } + {isReadOnly && <> + enableEditing()} /> + + } - {canBeConvertedToAttachment && } - {note.type === "render" && } - - - {isElectron && } - + {canBeConvertedToAttachment && } + {note.type === "render" && } + + + {isElectron && } + - parentComponent?.triggerCommand("showImportDialog", { noteId: note.noteId })} /> - noteContext?.notePath && parentComponent?.triggerCommand("showExportDialog", { - notePath: noteContext.notePath, - defaultType: "single" - })} /> - + parentComponent?.triggerCommand("showImportDialog", { noteId: note.noteId })} /> + noteContext?.notePath && parentComponent?.triggerCommand("showExportDialog", { + notePath: noteContext.notePath, + defaultType: "single" + })} /> + - - - - {(syncServerHost && isElectron) && - - } - + + + + {(syncServerHost && isElectron) && + + } + - - branches.deleteNotes([note.getParentBranches()[0].branchId])} - /> - + + branches.deleteNotes([note.getParentBranches()[0].branchId])} + /> + - - {glob.isDev && } - - ); + + {glob.isDev && } + + ); } function DevelopmentActions({ note, noteContext }: { note: FNote, noteContext?: NoteContext }) { @@ -129,46 +125,46 @@ function DevelopmentActions({ note, noteContext }: { note: FNote, noteContext?: throw new Error("Editor crashed."); }); }); - }}>Crash editor)} + }}>Crash editor)} - ) + ); } function CommandItem({ icon, text, title, command, disabled }: { icon: string, text: string, title?: string, command: CommandNames | (() => void), disabled?: boolean, destructive?: boolean }) { - return {text} + return {text}; } function ConvertToAttachment({ note }: { note: FNote }) { - return ( - { - if (!note || !(await dialog.confirm(t("note_actions.convert_into_attachment_prompt", { title: note.title })))) { - return; - } + return ( + { + if (!note || !(await dialog.confirm(t("note_actions.convert_into_attachment_prompt", { title: note.title })))) { + return; + } - const { attachment: newAttachment } = await server.post(`notes/${note.noteId}/convert-to-attachment`); + const { attachment: newAttachment } = await server.post(`notes/${note.noteId}/convert-to-attachment`); - if (!newAttachment) { - toast.showMessage(t("note_actions.convert_into_attachment_failed", { title: note.title })); - return; - } + if (!newAttachment) { + toast.showMessage(t("note_actions.convert_into_attachment_failed", { title: note.title })); + return; + } - toast.showMessage(t("note_actions.convert_into_attachment_successful", { title: newAttachment.title })); - await ws.waitForMaxKnownEntityChangeId(); - await appContext.tabManager.getActiveContext()?.setNote(newAttachment.ownerId, { - viewScope: { - viewMode: "attachments", - attachmentId: newAttachment.attachmentId - } - }); - }} - >{t("note_actions.convert_into_attachment")} - ) + toast.showMessage(t("note_actions.convert_into_attachment_successful", { title: newAttachment.title })); + await ws.waitForMaxKnownEntityChangeId(); + await appContext.tabManager.getActiveContext()?.setNote(newAttachment.ownerId, { + viewScope: { + viewMode: "attachments", + attachmentId: newAttachment.attachmentId + } + }); + }} + >{t("note_actions.convert_into_attachment")} + ); } diff --git a/apps/client/src/widgets/ribbon/Ribbon.tsx b/apps/client/src/widgets/ribbon/Ribbon.tsx index ee3fd3fcf..5889827fd 100644 --- a/apps/client/src/widgets/ribbon/Ribbon.tsx +++ b/apps/client/src/widgets/ribbon/Ribbon.tsx @@ -16,7 +16,7 @@ interface ComputedTab extends Indexed { shouldShow: boolean; } -export default function Ribbon() { +export default function Ribbon({ children }: { children?: preact.ComponentChildren }) { const { note, ntxId, hoistedNoteId, notePath, noteContext, componentId, isReadOnlyTemporarilyDisabled } = useNoteContext(); const noteType = useNoteProperty(note, "type"); const [ activeTabIndex, setActiveTabIndex ] = useState(); @@ -99,9 +99,7 @@ export default function Ribbon() { /> ))} -
- { note && } -
+ {children}
diff --git a/apps/client/src/widgets/ribbon/style.css b/apps/client/src/widgets/ribbon/style.css index ed51e89f2..49f5e10e1 100644 --- a/apps/client/src/widgets/ribbon/style.css +++ b/apps/client/src/widgets/ribbon/style.css @@ -417,14 +417,19 @@ body[dir=rtl] .attribute-list-editor { /* #endregion */ /* #region Experimental layout */ -body.experimental-feature-new-layout .ribbon-container { - display: flex; - flex-direction: column-reverse; - border-top: 1px solid var(--main-border-color); +body.experimental-feature-new-layout { + .ribbon-container { + display: flex; + flex-direction: column-reverse; + border-top: 1px solid var(--main-border-color); - .ribbon-tab-spacer, - .ribbon-button-container, - .ribbon-body { + .ribbon-tab-spacer, + .ribbon-body { + border-bottom: 0 !important; + } + } + + .ribbon-button-container { border-bottom: 0 !important; } }