fix(text): classic toolbar broken on mobile due to prior change

This commit is contained in:
Elian Doran 2025-11-22 11:37:34 +02:00
parent c76f368fa0
commit 9b3ca65492
No known key found for this signature in database
3 changed files with 48 additions and 36 deletions

View File

@ -10,16 +10,21 @@ import { TabContext } from "./ribbon-interface";
* The ribbon item is active by default for text notes, as long as they are not in read-only mode.
*
* ! The toolbar is not only used in the ribbon, but also in the quick edit feature.
* * The mobile toolbar is handled separately (see `MobileEditorToolbar`).
*/
export default function FormattingToolbar({ hidden, ntxId }: TabContext) {
const containerRef = useRef<HTMLDivElement>(null);
const [ textNoteEditorType ] = useTriliumOption("textNoteEditorType");
// Attach the toolbar from the CKEditor.
useTriliumEvent("textEditorRefreshed", ({ ntxId: eventNtxId, editor }) => {
if (eventNtxId !== ntxId) return;
if (eventNtxId !== ntxId || !containerRef.current) return;
const toolbar = editor.ui.view.toolbar?.element;
if (toolbar && containerRef.current) {
if (toolbar) {
containerRef.current.replaceChildren(toolbar);
} else {
containerRef.current.replaceChildren();
}
});

View File

@ -233,12 +233,6 @@ export default function EditableText({ note, parentComponent, ntxId, noteContext
onWatchdogStateChange={onWatchdogStateChange}
onChange={() => spacedUpdate.scheduleUpdate()}
onEditorInitialized={(editor) => {
console.log("Editor has been initialized!", parentComponent, editor);
if (isClassicEditor) {
setupClassicEditor(editor, parentComponent);
}
if (hasTouchBar) {
const handler = () => refreshTouchBarRef.current?.();
for (const event of [ "bold", "italic", "underline", "paragraph", "heading" ]) {
@ -303,26 +297,6 @@ function onNotificationWarning(data, evt) {
evt.stop();
}
function setupClassicEditor(editor: CKTextEditor, parentComponent: Component | undefined) {
if (!parentComponent) return;
if (utils.isMobile()) {
// Reposition all dropdowns to point upwards instead of downwards.
// See https://ckeditor.com/docs/ckeditor5/latest/examples/framework/bottom-toolbar-editor.html for more info.
const toolbarView = (editor as ClassicEditor).ui.view.toolbar;
for (const item of toolbarView.items) {
if (!("panelView" in item)) continue;
item.on("change:isOpen", () => {
if (!("isOpen" in item) || !item.isOpen) return;
// @ts-ignore
item.panelView.position = item.panelView.position.replace("s", "n");
});
}
}
}
function EditableTextTouchBar({ watchdogRef, refreshTouchBarRef }: { watchdogRef: RefObject<EditorWatchdog | null>, refreshTouchBarRef: RefObject<() => void> }) {
const [ headingSelectedIndex, setHeadingSelectedIndex ] = useState<number>();

View File

@ -1,7 +1,8 @@
import { MutableRef, useCallback, useEffect, useRef, useState } from "preact/hooks";
import { useNoteContext } from "../../react/hooks";
import { useNoteContext, useTriliumEvent } from "../../react/hooks";
import "./mobile_editor_toolbar.css";
import { isIOS } from "../../../services/utils";
import { CKTextEditor, ClassicEditor } from "@triliumnext/ckeditor5";
/**
* Handles the editing toolbar for CKEditor in mobile mode. The toolbar acts as a floating bar, with two different mechanism:
@ -10,12 +11,12 @@ import { isIOS } from "../../../services/utils";
* - On Android, the viewport change makes the keyboard resize the content area, all we have to do is to hide the tab bar and global menu (handled in the global style).
*/
export default function MobileEditorToolbar() {
const wrapperRef = useRef<HTMLDivElement>(null);
const { note, noteContext } = useNoteContext();
const containerRef = useRef<HTMLDivElement>(null);
const { note, noteContext, ntxId } = useNoteContext();
const [ shouldDisplay, setShouldDisplay ] = useState(false);
const [ dropdownActive, setDropdownActive ] = useState(false);
usePositioningOniOS(wrapperRef);
usePositioningOniOS(containerRef);
useEffect(() => {
noteContext?.isReadOnly().then(isReadOnly => {
@ -23,15 +24,28 @@ export default function MobileEditorToolbar() {
});
}, [ note ]);
// Attach the toolbar from the CKEditor.
useTriliumEvent("textEditorRefreshed", ({ ntxId: eventNtxId, editor }) => {
if (eventNtxId !== ntxId || !containerRef.current) return;
const toolbar = editor.ui.view.toolbar?.element;
repositionDropdowns(editor);
if (toolbar) {
containerRef.current.replaceChildren(toolbar);
} else {
containerRef.current.replaceChildren();
}
});
// Observe when a dropdown is expanded to apply a style that allows the dropdown to be visible, since we can't have the element both visible and the toolbar scrollable.
useEffect(() => {
if (!wrapperRef.current) return;
if (!containerRef.current) return;
const observer = new MutationObserver(e => {
setDropdownActive(e.map((e) => (e.target as any).ariaExpanded === "true").reduce((acc, e) => acc && e));
});
observer.observe(wrapperRef.current, {
observer.observe(containerRef.current, {
attributeFilter: ["aria-expanded"],
subtree: true
});
@ -41,7 +55,7 @@ export default function MobileEditorToolbar() {
return (
<div className={`classic-toolbar-outer-container ${!shouldDisplay ? "hidden-ext" : "visible"} ${isIOS() ? "ios" : ""}`}>
<div ref={wrapperRef} className={`classic-toolbar-widget ${dropdownActive ? "dropdown-active" : ""}`}></div>
<div ref={containerRef} className={`classic-toolbar-widget ${dropdownActive ? "dropdown-active" : ""}`}></div>
</div>
)
}
@ -65,3 +79,22 @@ function usePositioningOniOS(wrapperRef: MutableRef<HTMLDivElement | null>) {
};
}, []);
}
/**
* Reposition all dropdowns to point upwards instead of downwards.
* See https://ckeditor.com/docs/ckeditor5/latest/examples/framework/bottom-toolbar-editor.html for more info.
* @param editor
*/
function repositionDropdowns(editor: CKTextEditor) {
const toolbarView = (editor as ClassicEditor).ui.view.toolbar;
for (const item of toolbarView.items) {
if (!("panelView" in item)) continue;
item.on("change:isOpen", () => {
if (!("isOpen" in item) || !item.isOpen) return;
// @ts-ignore
item.panelView.position = item.panelView.position.replace("s", "n");
});
}
}