From 45fbcec805bb6c4576d2d21811de2cb6d523f58e Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 21 Aug 2025 18:29:13 +0300 Subject: [PATCH] feat(react/ribbon): port base structure --- apps/client/src/layouts/desktop_layout.tsx | 25 +--- .../widgets/containers/ribbon_container.ts | 123 +----------------- apps/client/src/widgets/ribbon/Ribbon.tsx | 37 ++++++ apps/client/src/widgets/ribbon/style.css | 94 +++++++++++++ .../ribbon_widgets/basic_properties.ts | 2 - 5 files changed, 136 insertions(+), 145 deletions(-) create mode 100644 apps/client/src/widgets/ribbon/Ribbon.tsx create mode 100644 apps/client/src/widgets/ribbon/style.css diff --git a/apps/client/src/layouts/desktop_layout.tsx b/apps/client/src/layouts/desktop_layout.tsx index 4b094ba3b..6452dfb63 100644 --- a/apps/client/src/layouts/desktop_layout.tsx +++ b/apps/client/src/layouts/desktop_layout.tsx @@ -73,6 +73,7 @@ import ToggleReadOnlyButton from "../widgets/floating_buttons/toggle_read_only_b import PngExportButton from "../widgets/floating_buttons/png_export_button.js"; import RefreshButton from "../widgets/floating_buttons/refresh_button.js"; import { applyModals } from "./layout_commons.js"; +import Ribbon from "../widgets/ribbon/Ribbon.jsx"; export default class DesktopLayout { @@ -159,29 +160,7 @@ export default class DesktopLayout { .child(new ClosePaneButton()) .child(new CreatePaneButton()) ) - .child( - new RibbonContainer() - // the order of the widgets matter. Some of these want to "activate" themselves - // when visible. When this happens to multiple of them, the first one "wins". - // promoted attributes should always win. - .ribbon(new ClassicEditorToolbar()) - .ribbon(new ScriptExecutorWidget()) - .ribbon(new SearchDefinitionWidget()) - .ribbon(new EditedNotesWidget()) - .ribbon(new BookPropertiesWidget()) - .ribbon(new NotePropertiesWidget()) - .ribbon(new FilePropertiesWidget()) - .ribbon(new ImagePropertiesWidget()) - .ribbon(new BasicPropertiesWidget()) - .ribbon(new OwnedAttributeListWidget()) - .ribbon(new InheritedAttributesWidget()) - .ribbon(new NotePathsWidget()) - .ribbon(new NoteMapRibbonWidget()) - .ribbon(new SimilarNotesWidget()) - .ribbon(new NoteInfoWidget()) - .button(new RevisionsButton()) - .button(new NoteActionsWidget()) - ) + .child() .child(new SharedInfoWidget()) .child(new WatchedFileUpdateStatusWidget()) .child( diff --git a/apps/client/src/widgets/containers/ribbon_container.ts b/apps/client/src/widgets/containers/ribbon_container.ts index 9aee7bb67..9bd225d39 100644 --- a/apps/client/src/widgets/containers/ribbon_container.ts +++ b/apps/client/src/widgets/containers/ribbon_container.ts @@ -7,116 +7,6 @@ import type { NoteType } from "../../entities/fnote.js"; import type { EventData, EventNames } from "../../components/app_context.js"; import type NoteActionsWidget from "../buttons/note_actions.js"; -const TPL = /*html*/` -
- - -
-
-
-
- -
-
`; - type ButtonWidget = (CommandButtonWidget | NoteActionsWidget); export default class RibbonContainer extends NoteContextAwareWidget { @@ -160,8 +50,6 @@ export default class RibbonContainer extends NoteContextAwareWidget { } doRender() { - this.$widget = $(TPL); - this.$tabContainer = this.$widget.find(".ribbon-tab-container"); this.$buttonContainer = this.$widget.find(".ribbon-button-container"); this.$bodyContainer = this.$widget.find(".ribbon-body-container"); @@ -241,16 +129,11 @@ export default class RibbonContainer extends NoteContextAwareWidget { .attr("data-ribbon-component-id", ribbonWidget.componentId) .attr("data-ribbon-component-name", (ribbonWidget as any).name as string) // TODO: base class for ribbon widgets .append( - $('') - .addClass(ret.icon) - .attr("title", ret.title) + $('') .attr("data-toggle-command", (ribbonWidget as any).toggleCommand) - ) // TODO: base class - .append(" ") - .append($('').text(ret.title)); + ) - this.$tabContainer.append($ribbonTitle); - this.$tabContainer.append('
'); + this.$tabContainer.append(''); if (ret.activate && !this.lastActiveComponentId && !$ribbonTabToActivate && !noExplicitActivation) { $ribbonTabToActivate = $ribbonTitle; diff --git a/apps/client/src/widgets/ribbon/Ribbon.tsx b/apps/client/src/widgets/ribbon/Ribbon.tsx new file mode 100644 index 000000000..48dcdcf1a --- /dev/null +++ b/apps/client/src/widgets/ribbon/Ribbon.tsx @@ -0,0 +1,37 @@ +import { t } from "../../services/i18n"; +import "./style.css"; + +export default function Ribbon() { + return ( +
+
+
+ +
+
+
+ +
+
+ ) +} + +function RibbonTab({ icon, title }: { icon: string; title: string }) { + return ( + <> +
+ +   + {title} +
+ +
+ + ) +} \ No newline at end of file diff --git a/apps/client/src/widgets/ribbon/style.css b/apps/client/src/widgets/ribbon/style.css new file mode 100644 index 000000000..9c72f13be --- /dev/null +++ b/apps/client/src/widgets/ribbon/style.css @@ -0,0 +1,94 @@ +.ribbon-container { + margin-bottom: 5px; +} + +.ribbon-top-row { + display: flex; + min-height: 36px; +} + +.ribbon-tab-container { + display: flex; + flex-direction: row; + justify-content: center; + margin-left: 10px; + flex-grow: 1; + flex-flow: row wrap; +} + +.ribbon-tab-title { + color: var(--muted-text-color); + border-bottom: 1px solid var(--main-border-color); + min-width: 24px; + flex-basis: 24px; + max-width: max-content; + flex-grow: 10; +} + +.ribbon-tab-title .bx { + font-size: 150%; + position: relative; + top: 3px; +} + +.ribbon-tab-title.active { + color: var(--main-text-color); + border-bottom: 3px solid var(--main-text-color); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.ribbon-tab-title:hover { + cursor: pointer; +} + +.ribbon-tab-title:hover { + color: var(--main-text-color); +} + +.ribbon-tab-title:first-of-type { + padding-left: 10px; +} + +.ribbon-tab-spacer { + flex-basis: 0; + min-width: 0; + max-width: 35px; + flex-grow: 1; + border-bottom: 1px solid var(--main-border-color); +} + +.ribbon-tab-spacer:last-of-type { + flex-grow: 1; + flex-basis: 0; + min-width: 0; + max-width: 10000px; +} + +.ribbon-button-container { + display: flex; + border-bottom: 1px solid var(--main-border-color); + margin-right: 5px; +} + +.ribbon-button-container > * { + position: relative; + top: -3px; + margin-left: 10px; +} + +.ribbon-body { + display: none; + border-bottom: 1px solid var(--main-border-color); + margin-left: 10px; + margin-right: 5px; /* needs to have this value so that the bottom border is the same width as the top one */ +} + +.ribbon-body.active { + display: block; +} + +.ribbon-tab-title.active .ribbon-tab-title-label { + display: inline; +} \ No newline at end of file diff --git a/apps/client/src/widgets/ribbon_widgets/basic_properties.ts b/apps/client/src/widgets/ribbon_widgets/basic_properties.ts index b78e80a8d..aaa2bf934 100644 --- a/apps/client/src/widgets/ribbon_widgets/basic_properties.ts +++ b/apps/client/src/widgets/ribbon_widgets/basic_properties.ts @@ -99,8 +99,6 @@ export default class BasicPropertiesWidget extends NoteContextAwareWidget { getTitle() { return { show: !this.note?.isLaunchBarConfig(), - title: t("basic_properties.basic_properties"), - icon: "bx bx-slider" }; }