diff --git a/apps/client/src/layouts/layout_commons.tsx b/apps/client/src/layouts/layout_commons.tsx
index 87d446e18..9b9ac886f 100644
--- a/apps/client/src/layouts/layout_commons.tsx
+++ b/apps/client/src/layouts/layout_commons.tsx
@@ -25,7 +25,6 @@ import IncorrectCpuArchDialog from "../widgets/dialogs/incorrect_cpu_arch.js";
import PopupEditorDialog from "../widgets/dialogs/popup_editor.js";
import FlexContainer from "../widgets/containers/flex_container.js";
import NoteIconWidget from "../widgets/note_icon";
-import ClassicEditorToolbar from "../widgets/ribbon_widgets/classic_editor_toolbar.js";
import PromotedAttributesWidget from "../widgets/ribbon_widgets/promoted_attributes.js";
import NoteDetailWidget from "../widgets/note_detail.js";
import NoteListWidget from "../widgets/note_list.js";
@@ -63,7 +62,7 @@ export function applyModals(rootContainer: RootContainer) {
.cssBlock(".title-row > * { margin: 5px; }")
.child()
.child())
- .child(new ClassicEditorToolbar())
+ // .child(new ClassicEditorToolbar())
.child(new PromotedAttributesWidget())
.child(new NoteDetailWidget())
.child(new NoteListWidget(true)))
diff --git a/apps/client/src/widgets/ribbon/FormattingTab.tsx b/apps/client/src/widgets/ribbon/FormattingTab.tsx
new file mode 100644
index 000000000..d273044e4
--- /dev/null
+++ b/apps/client/src/widgets/ribbon/FormattingTab.tsx
@@ -0,0 +1,21 @@
+import { CSSProperties } from "preact/compat";
+
+/**
+ * Handles the editing toolbar when the CKEditor is in decoupled mode.
+ *
+ * This toolbar is only enabled if the user has selected the classic CKEditor.
+ *
+ * The ribbon item is active by default for text notes, as long as they are not in read-only mode.
+ */
+export default function FormattingTab({ hidden }) {
+ const style: CSSProperties = {};
+ if (hidden) {
+ style.display = "none";
+ }
+
+ return (
+
+
+
+ )
+};
\ No newline at end of file
diff --git a/apps/client/src/widgets/ribbon/Ribbon.tsx b/apps/client/src/widgets/ribbon/Ribbon.tsx
index 15510e517..642da63a0 100644
--- a/apps/client/src/widgets/ribbon/Ribbon.tsx
+++ b/apps/client/src/widgets/ribbon/Ribbon.tsx
@@ -1,25 +1,41 @@
import { useMemo, useState } from "preact/hooks";
-import FNote from "../../entities/fnote";
import { t } from "../../services/i18n";
import { useNoteContext } from "../react/hooks";
import "./style.css";
import { VNode } from "preact";
import BasicPropertiesTab from "./BasicPropertiesTab";
+import FormattingTab from "./FormattingTab";
import { numberObjectsInPlace } from "../../services/utils";
import { TabContext } from "./ribbon-interface";
+import options from "../../services/options";
+import { CommandNames } from "../../components/app_context";
+import FNote from "../../entities/fnote";
+
+interface TitleContext {
+ note: FNote | null | undefined;
+}
+
interface TabConfiguration {
- title: string | ((context: TabContext) => string);
+ title: string | ((context: TitleContext) => string);
icon: string;
// TODO: Mark as required after porting them all.
content?: (context: TabContext) => VNode;
- show?: (context: TabContext) => boolean;
+ show?: (context: TitleContext) => boolean;
+ toggleCommand?: CommandNames;
+ /**
+ * By default the tab content will not be rendered unless the tab is active (i.e. selected by the user). Setting to `true` will ensure that the tab is rendered even when inactive, for cases where the tab needs to be accessible at all times (e.g. for the detached editor toolbar).
+ */
+ stayInDom?: boolean;
}
const TAB_CONFIGURATION = numberObjectsInPlace([
{
title: t("classic_editor_toolbar.title"),
- icon: "bx bx-text"
- // ClassicEditorToolbar
+ icon: "bx bx-text",
+ show: ({ note }) => note?.type === "text" && options.get("textNoteEditorType") === "ckeditor-classic",
+ toggleCommand: "toggleRibbonTabClassicEditor",
+ content: FormattingTab,
+ stayInDom: true
},
{
title: ({ note }) => note?.isTriliumSqlite() ? t("script_executor.query") : t("script_executor.script"),
@@ -61,7 +77,8 @@ const TAB_CONFIGURATION = numberObjectsInPlace([
title: t("basic_properties.basic_properties"),
icon: "bx bx-slider",
content: BasicPropertiesTab,
- show: ({note}) => !note?.isLaunchBarConfig()
+ show: ({note}) => !note?.isLaunchBarConfig(),
+ toggleCommand: "toggleRibbonTabBasicProperties"
},
{
// OwnedAttributeListWidget
@@ -97,10 +114,9 @@ const TAB_CONFIGURATION = numberObjectsInPlace([
export default function Ribbon() {
const { note } = useNoteContext();
- const context: TabContext = { note };
+ const titleContext: TitleContext = { note };
const [ activeTabIndex, setActiveTabIndex ] = useState();
- const filteredTabs = useMemo(() => TAB_CONFIGURATION.filter(tab => tab.show?.(context)), [ context, note ])
- const activeTabConfiguration = activeTabIndex ? filteredTabs.find(tab => tab.index === activeTabIndex) : undefined;
+ const filteredTabs = useMemo(() => TAB_CONFIGURATION.filter(tab => tab.show?.(titleContext)), [ titleContext, note ])
return (
@@ -109,7 +125,7 @@ export default function Ribbon() {
{filteredTabs.map(({ title, icon, index }) => (
{
if (activeTabIndex !== index) {
@@ -127,7 +143,14 @@ export default function Ribbon() {
- {activeTabConfiguration?.content && activeTabConfiguration.content({ note })}
+ {filteredTabs.map(tab => {
+ const isActive = tab.index === activeTabIndex;
+ if (!isActive && !tab.stayInDom) {
+ return;
+ }
+
+ return tab?.content && tab.content({ note, hidden: !isActive });
+ })}
diff --git a/apps/client/src/widgets/ribbon/ribbon-interface.ts b/apps/client/src/widgets/ribbon/ribbon-interface.ts
index c321675a3..030361fce 100644
--- a/apps/client/src/widgets/ribbon/ribbon-interface.ts
+++ b/apps/client/src/widgets/ribbon/ribbon-interface.ts
@@ -2,4 +2,5 @@ import FNote from "../../entities/fnote";
export interface TabContext {
note: FNote | null | undefined;
+ hidden: boolean;
}
diff --git a/apps/client/src/widgets/ribbon/style.css b/apps/client/src/widgets/ribbon/style.css
index f29d1cf4a..594a3e6e2 100644
--- a/apps/client/src/widgets/ribbon/style.css
+++ b/apps/client/src/widgets/ribbon/style.css
@@ -93,6 +93,8 @@
display: inline;
}
+/* #region Basic Properties */
+
.basic-properties-widget {
padding: 0px 12px 6px 12px;
display: flex;
@@ -124,4 +126,25 @@
.editability-dropdown {
width: 300px;
-}
\ No newline at end of file
+}
+
+/* #endregion */
+
+/* #region Formatting Toolbar */
+
+.classic-toolbar-widget {
+ --ck-color-toolbar-background: transparent;
+ --ck-color-button-default-background: transparent;
+ --ck-color-button-default-disabled-background: transparent;
+ min-height: 39px;
+}
+
+.classic-toolbar-widget .ck.ck-toolbar {
+ border: none;
+}
+
+.classic-toolbar-widget .ck.ck-button.ck-disabled {
+ opacity: 0.3;
+}
+
+/* #endregion */
\ No newline at end of file
diff --git a/apps/client/src/widgets/ribbon_widgets/classic_editor_toolbar.ts b/apps/client/src/widgets/ribbon_widgets/classic_editor_toolbar.ts
deleted file mode 100644
index f93b78ff8..000000000
--- a/apps/client/src/widgets/ribbon_widgets/classic_editor_toolbar.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-import { t } from "../../services/i18n.js";
-import options from "../../services/options.js";
-import utils from "../../services/utils.js";
-import NoteContextAwareWidget from "../note_context_aware_widget.js";
-
-const TPL = /*html*/`\
-
-
-
-`;
-
-/**
- * Handles the editing toolbar when the CKEditor is in decoupled mode.
- *
- *
- * This toolbar is only enabled if the user has selected the classic CKEditor.
- *
- *
- * The ribbon item is active by default for text notes, as long as they are not in read-only mode.
- */
-export default class ClassicEditorToolbar extends NoteContextAwareWidget {
-
- get name() {
- return "classicEditor";
- }
-
- get toggleCommand() {
- return "toggleRibbonTabClassicEditor";
- }
-
- doRender() {
- this.$widget = $(TPL);
- this.contentSized();
- }
-
- isEnabled(): boolean | null | undefined {
- if (options.get("textNoteEditorType") !== "ckeditor-classic") {
- return false;
- }
-
- if (!this.note || this.note.type !== "text") {
- return false;
- }
-
- return true;
- }
-
- async getTitle() {
- return {
- show: await this.#shouldDisplay(),
- activate: true,
- };
- }
-
- async #shouldDisplay() {
- if (!this.isEnabled()) {
- return false;
- }
-
- if (await this.noteContext?.isReadOnly()) {
- return false;
- }
-
- return true;
- }
-
-}