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; - } - -}