2025-11-22 19:07:50 +02:00
|
|
|
import "./PopupEditor.css";
|
2025-12-10 11:10:26 +02:00
|
|
|
|
2025-11-22 20:08:37 +02:00
|
|
|
import { ComponentChildren } from "preact";
|
2025-12-10 11:10:26 +02:00
|
|
|
import { useContext, useEffect, useMemo, useRef, useState } from "preact/hooks";
|
|
|
|
|
|
|
|
|
|
import appContext from "../../components/app_context";
|
|
|
|
|
import NoteContext from "../../components/note_context";
|
|
|
|
|
import froca from "../../services/froca";
|
|
|
|
|
import { t } from "../../services/i18n";
|
|
|
|
|
import tree from "../../services/tree";
|
|
|
|
|
import utils from "../../services/utils";
|
2025-11-22 21:00:55 +02:00
|
|
|
import NoteList from "../collections/NoteList";
|
2025-11-24 21:45:33 +02:00
|
|
|
import FloatingButtons from "../FloatingButtons";
|
2025-11-24 21:56:16 +02:00
|
|
|
import { DESKTOP_FLOATING_BUTTONS, MOBILE_FLOATING_BUTTONS, POPUP_HIDDEN_FLOATING_BUTTONS } from "../FloatingButtonsDefinitions";
|
2025-12-10 11:10:26 +02:00
|
|
|
import NoteIcon from "../note_icon";
|
|
|
|
|
import NoteTitleWidget from "../note_title";
|
|
|
|
|
import NoteDetail from "../NoteDetail";
|
|
|
|
|
import PromotedAttributes from "../PromotedAttributes";
|
|
|
|
|
import { useNoteContext, useNoteLabel, useTriliumEvent } from "../react/hooks";
|
|
|
|
|
import Modal from "../react/Modal";
|
|
|
|
|
import { NoteContextContext, ParentComponent } from "../react/react_utils";
|
2025-11-25 08:34:35 +02:00
|
|
|
import ReadOnlyNoteInfoBar from "../ReadOnlyNoteInfoBar";
|
2025-12-10 11:10:26 +02:00
|
|
|
import StandaloneRibbonAdapter from "../ribbon/components/StandaloneRibbonAdapter";
|
|
|
|
|
import FormattingToolbar from "../ribbon/FormattingToolbar";
|
2025-11-29 12:16:42 +02:00
|
|
|
import MobileEditorToolbar from "../type_widgets/text/mobile_editor_toolbar";
|
2025-12-13 10:54:19 +02:00
|
|
|
import NoteBadges from "../layout/NoteBadges";
|
2025-12-10 11:10:26 +02:00
|
|
|
import { isExperimentalFeatureEnabled } from "../../services/experimental_features";
|
|
|
|
|
|
|
|
|
|
const isNewLayout = isExperimentalFeatureEnabled("new-layout");
|
2025-11-22 19:07:50 +02:00
|
|
|
|
|
|
|
|
export default function PopupEditor() {
|
|
|
|
|
const [ shown, setShown ] = useState(false);
|
2025-11-22 19:34:14 +02:00
|
|
|
const parentComponent = useContext(ParentComponent);
|
2025-11-22 20:08:37 +02:00
|
|
|
const [ noteContext, setNoteContext ] = useState(new NoteContext("_popup-editor"));
|
2025-11-24 21:56:16 +02:00
|
|
|
const isMobile = utils.isMobile();
|
|
|
|
|
const items = useMemo(() => {
|
|
|
|
|
const baseItems = isMobile ? MOBILE_FLOATING_BUTTONS : DESKTOP_FLOATING_BUTTONS;
|
|
|
|
|
return baseItems.filter(item => !POPUP_HIDDEN_FLOATING_BUTTONS.includes(item));
|
|
|
|
|
}, [ isMobile ]);
|
2025-11-22 19:11:20 +02:00
|
|
|
|
|
|
|
|
useTriliumEvent("openInPopup", async ({ noteIdOrPath }) => {
|
2025-11-22 20:08:37 +02:00
|
|
|
const noteContext = new NoteContext("_popup-editor");
|
2025-11-25 08:19:48 +02:00
|
|
|
|
2025-11-25 08:24:26 +02:00
|
|
|
const noteId = tree.getNoteIdAndParentIdFromUrl(noteIdOrPath);
|
2025-11-25 08:19:48 +02:00
|
|
|
if (!noteId.noteId) return;
|
|
|
|
|
const note = await froca.getNote(noteId.noteId);
|
|
|
|
|
if (!note) return;
|
|
|
|
|
|
|
|
|
|
const hasUserSetNoteReadOnly = note.hasLabel("readOnly");
|
2025-11-22 19:11:20 +02:00
|
|
|
await noteContext.setNote(noteIdOrPath, {
|
|
|
|
|
viewScope: {
|
2025-11-25 08:19:48 +02:00
|
|
|
// Override auto-readonly notes to be editable, but respect user's choice to have a read-only note.
|
|
|
|
|
readOnlyTemporarilyDisabled: !hasUserSetNoteReadOnly
|
2025-11-22 19:11:20 +02:00
|
|
|
}
|
|
|
|
|
});
|
2025-11-22 19:07:50 +02:00
|
|
|
|
2025-11-22 20:08:37 +02:00
|
|
|
setNoteContext(noteContext);
|
2025-11-22 19:07:50 +02:00
|
|
|
setShown(true);
|
|
|
|
|
});
|
|
|
|
|
|
2025-11-22 21:37:41 +02:00
|
|
|
// Add a global class to be able to handle issues with z-index due to rendering in a popup.
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
document.body.classList.toggle("popup-editor-open", shown);
|
|
|
|
|
}, [shown]);
|
|
|
|
|
|
2025-11-22 19:07:50 +02:00
|
|
|
return (
|
2025-11-22 19:25:27 +02:00
|
|
|
<NoteContextContext.Provider value={noteContext}>
|
2025-11-22 20:08:37 +02:00
|
|
|
<DialogWrapper>
|
|
|
|
|
<Modal
|
2025-12-10 11:10:26 +02:00
|
|
|
title={<>
|
|
|
|
|
<TitleRow />
|
2025-12-13 10:54:19 +02:00
|
|
|
{isNewLayout && <NoteBadges />}
|
2025-12-10 11:10:26 +02:00
|
|
|
</>}
|
2025-11-30 01:54:28 +02:00
|
|
|
customTitleBarButtons={[{
|
|
|
|
|
iconClassName: "bx-expand-alt",
|
2025-11-30 19:47:17 +02:00
|
|
|
title: t("popup-editor.maximize"),
|
|
|
|
|
onClick: async () => {
|
|
|
|
|
if (!noteContext.noteId) return;
|
|
|
|
|
const { noteId, hoistedNoteId } = noteContext;
|
|
|
|
|
await appContext.tabManager.openInNewTab(noteId, hoistedNoteId, true);
|
|
|
|
|
setShown(false);
|
|
|
|
|
}
|
2025-11-30 01:54:28 +02:00
|
|
|
}]}
|
2025-11-22 20:08:37 +02:00
|
|
|
className="popup-editor-dialog"
|
|
|
|
|
size="lg"
|
|
|
|
|
show={shown}
|
2025-12-10 11:10:26 +02:00
|
|
|
onShown={() => parentComponent?.handleEvent("focusOnDetail", { ntxId: noteContext.ntxId })}
|
2025-11-22 20:08:37 +02:00
|
|
|
onHidden={() => setShown(false)}
|
2025-11-24 19:38:18 +02:00
|
|
|
keepInDom // needed for faster loading
|
2025-11-29 11:47:36 +02:00
|
|
|
noFocus // automatic focus breaks block popup
|
2025-11-22 20:08:37 +02:00
|
|
|
>
|
2025-12-10 11:10:26 +02:00
|
|
|
{!isNewLayout && <ReadOnlyNoteInfoBar />}
|
2025-11-23 13:44:24 +02:00
|
|
|
<PromotedAttributes />
|
2025-11-29 12:16:42 +02:00
|
|
|
|
|
|
|
|
{isMobile
|
2025-12-10 11:10:26 +02:00
|
|
|
? <MobileEditorToolbar inPopupEditor />
|
|
|
|
|
: <StandaloneRibbonAdapter component={FormattingToolbar} />}
|
2025-11-29 12:16:42 +02:00
|
|
|
|
2025-11-24 21:56:16 +02:00
|
|
|
<FloatingButtons items={items} />
|
2025-11-22 20:08:37 +02:00
|
|
|
<NoteDetail />
|
2025-11-22 21:00:55 +02:00
|
|
|
<NoteList media="screen" displayOnlyCollections />
|
2025-11-22 20:08:37 +02:00
|
|
|
</Modal>
|
|
|
|
|
</DialogWrapper>
|
2025-11-22 19:25:27 +02:00
|
|
|
</NoteContextContext.Provider>
|
2025-12-10 11:10:26 +02:00
|
|
|
);
|
2025-11-22 19:07:50 +02:00
|
|
|
}
|
2025-11-22 19:17:50 +02:00
|
|
|
|
2025-11-22 20:08:37 +02:00
|
|
|
export function DialogWrapper({ children }: { children: ComponentChildren }) {
|
2025-11-22 21:37:41 +02:00
|
|
|
const { note } = useNoteContext();
|
2025-11-22 20:08:37 +02:00
|
|
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
2025-11-29 17:19:37 +02:00
|
|
|
useNoteLabel(note, "color"); // to update color class
|
2025-11-22 20:08:37 +02:00
|
|
|
|
|
|
|
|
return (
|
2025-11-29 17:19:37 +02:00
|
|
|
<div ref={wrapperRef} class={`quick-edit-dialog-wrapper ${note?.getColorClass() ?? ""}`}>
|
2025-11-22 20:08:37 +02:00
|
|
|
{children}
|
|
|
|
|
</div>
|
2025-12-10 11:10:26 +02:00
|
|
|
);
|
2025-11-22 20:08:37 +02:00
|
|
|
}
|
|
|
|
|
|
2025-11-22 19:17:50 +02:00
|
|
|
export function TitleRow() {
|
|
|
|
|
return (
|
|
|
|
|
<div className="title-row">
|
|
|
|
|
<NoteIcon />
|
|
|
|
|
<NoteTitleWidget />
|
|
|
|
|
</div>
|
2025-12-10 11:10:26 +02:00
|
|
|
);
|
2025-11-22 19:17:50 +02:00
|
|
|
}
|