From aac4316fb85e6be71102e5871a1563b6c2e90c41 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 20 Dec 2025 10:33:28 +0200 Subject: [PATCH] feat(right_pane): render title bar --- apps/client/src/widgets/react/hooks.tsx | 17 ++++---- .../widgets/sidebar/RightPanelContainer.tsx | 39 +++++++++++++++++-- .../src/widgets/sidebar/RightPanelWidget.tsx | 11 +++--- 3 files changed, 51 insertions(+), 16 deletions(-) diff --git a/apps/client/src/widgets/react/hooks.tsx b/apps/client/src/widgets/react/hooks.tsx index 9540cb8ad..d6b0db34c 100644 --- a/apps/client/src/widgets/react/hooks.tsx +++ b/apps/client/src/widgets/react/hooks.tsx @@ -605,10 +605,11 @@ export function useNoteBlob(note: FNote | null | undefined, componentId?: string return blob; } -export function useLegacyWidget(widgetFactory: () => T, { noteContext, containerClassName, containerStyle }: { +export function useLegacyWidget(widgetFactory: () => T, { noteContext, containerClassName, containerStyle, noAttach }: { noteContext?: NoteContext; containerClassName?: string; containerStyle?: CSSProperties; + noAttach?: boolean; } = {}): [VNode, T] { const ref = useRef(null); const parentComponent = useContext(ParentComponent); @@ -627,22 +628,24 @@ export function useLegacyWidget(widgetFactory: () => T, { const renderedWidget = widget.render(); return [ widget, renderedWidget ]; - }, []); + }, [ noteContext, parentComponent, widgetFactory]); // Attach the widget to the parent. useEffect(() => { - if (ref.current) { - ref.current.innerHTML = ""; - renderedWidget.appendTo(ref.current); + if (noAttach) return; + const parentContainer = ref.current; + if (parentContainer) { + parentContainer.replaceChildren(); + renderedWidget.appendTo(parentContainer); } - }, [ renderedWidget ]); + }, [ renderedWidget, noAttach ]); // Inject the note context. useEffect(() => { if (noteContext && widget instanceof NoteContextAwareWidget) { widget.activeContextChangedEvent({ noteContext }); } - }, [ noteContext ]); + }, [ noteContext, widget ]); useDebugValue(widget); diff --git a/apps/client/src/widgets/sidebar/RightPanelContainer.tsx b/apps/client/src/widgets/sidebar/RightPanelContainer.tsx index 85010e850..fd339563a 100644 --- a/apps/client/src/widgets/sidebar/RightPanelContainer.tsx +++ b/apps/client/src/widgets/sidebar/RightPanelContainer.tsx @@ -2,7 +2,7 @@ import "./RightPanelContainer.css"; import Split from "@triliumnext/split.js"; -import { useEffect } from "preact/hooks"; +import { useEffect, useRef } from "preact/hooks"; import { t } from "../../services/i18n"; import options from "../../services/options"; @@ -11,7 +11,9 @@ import BasicWidget from "../basic_widget"; import Button from "../react/Button"; import { useActiveNoteContext, useLegacyWidget, useNoteProperty, useTriliumOptionBool, useTriliumOptionJson } from "../react/hooks"; import Icon from "../react/Icon"; +import LegacyRightPanelWidget from "../right_panel_widget"; import HighlightsList from "./HighlightsList"; +import RightPanelWidget from "./RightPanelWidget"; import TableOfContents from "./TableOfContents"; const MIN_WIDTH_PERCENT = 5; @@ -67,7 +69,36 @@ function useSplit(visible: boolean) { }, [ visible ]); } -function CustomWidget({ originalWidget }: { originalWidget: BasicWidget }) { - const [ el ] = useLegacyWidget(() => originalWidget); - return <>{el}; +function CustomWidget({ originalWidget }: { originalWidget: LegacyRightPanelWidget }) { + const containerRef = useRef(null); + const [ el ] = useLegacyWidget(() => { + // Monkey-patch the original widget by replacing the default initialization logic. + originalWidget.doRender = function doRender(this: LegacyRightPanelWidget) { + if (!containerRef.current) { + this.$widget = $("
"); + return; + }; + this.$widget = $(containerRef.current); + this.$body = this.$widget.find(".card-body"); + const renderResult = this.doRenderBody(); + if (typeof renderResult === "object" && "catch" in renderResult) { + this.initialized = renderResult.catch((e) => { + this.logRenderingError(e); + }); + } else { + this.initialized = Promise.resolve(); + } + }; + + return originalWidget; + }, { + noAttach: true + }); + return ( + {el} + ); } diff --git a/apps/client/src/widgets/sidebar/RightPanelWidget.tsx b/apps/client/src/widgets/sidebar/RightPanelWidget.tsx index 6b3a56925..a64b636d6 100644 --- a/apps/client/src/widgets/sidebar/RightPanelWidget.tsx +++ b/apps/client/src/widgets/sidebar/RightPanelWidget.tsx @@ -1,8 +1,8 @@ import clsx from "clsx"; -import { ComponentChildren } from "preact"; -import { useContext, useRef, useState } from "preact/hooks"; +import { ComponentChildren, RefObject } from "preact"; +import { useContext, useState } from "preact/hooks"; -import { useTriliumOptionJson } from "../react/hooks"; +import { useSyncedRef, useTriliumOptionJson } from "../react/hooks"; import Icon from "../react/Icon"; import { ParentComponent } from "../react/react_utils"; @@ -11,12 +11,13 @@ interface RightPanelWidgetProps { title: string; children: ComponentChildren; buttons?: ComponentChildren; + containerRef?: RefObject; } -export default function RightPanelWidget({ id, title, buttons, children }: RightPanelWidgetProps) { +export default function RightPanelWidget({ id, title, buttons, children, containerRef: externalContainerRef }: RightPanelWidgetProps) { const [ rightPaneCollapsedItems, setRightPaneCollapsedItems ] = useTriliumOptionJson("rightPaneCollapsedItems"); const [ expanded, setExpanded ] = useState(!rightPaneCollapsedItems.includes(id)); - const containerRef = useRef(null); + const containerRef = useSyncedRef(externalContainerRef, null); const parentComponent = useContext(ParentComponent); if (parentComponent) {