mirror of
https://github.com/zadam/trilium.git
synced 2025-10-06 21:47:31 +08:00
refactor(react/type_widgets): deduplicate containers
This commit is contained in:
parent
22069d0aef
commit
c209a699ea
21 changed files with 206 additions and 183 deletions
|
@ -103,6 +103,7 @@ export default function NoteDetail() {
|
|||
key={type}
|
||||
type={type as ExtendedNoteType}
|
||||
isVisible={activeNoteType === type}
|
||||
isFullHeight={isFullHeight}
|
||||
props={props}
|
||||
/>
|
||||
})}
|
||||
|
@ -114,7 +115,7 @@ export default function NoteDetail() {
|
|||
* Wraps a single note type widget, in order to keep it in the DOM even after the user has switched away to another note type. This allows faster loading of the same note type again. The properties are cached, so that they are updated only
|
||||
* while the widget is visible, to avoid rendering in the background. When not visible, the DOM element is simply hidden.
|
||||
*/
|
||||
function NoteDetailWrapper({ Element, type, isVisible, props }: { Element: (props: TypeWidgetProps) => VNode, type: ExtendedNoteType, isVisible: boolean, props: TypeWidgetProps }) {
|
||||
function NoteDetailWrapper({ Element, type, isVisible, isFullHeight, props }: { Element: (props: TypeWidgetProps) => VNode, type: ExtendedNoteType, isVisible: boolean, isFullHeight: boolean, props: TypeWidgetProps }) {
|
||||
const [ cachedProps, setCachedProps ] = useState(props);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -125,10 +126,15 @@ function NoteDetailWrapper({ Element, type, isVisible, props }: { Element: (prop
|
|||
}
|
||||
}, [ isVisible ]);
|
||||
|
||||
const typeMapping = TYPE_MAPPINGS[type];
|
||||
return (
|
||||
<div className={`note-detail-${type}`} style={{
|
||||
display: !isVisible ? "none" : ""
|
||||
}}>
|
||||
<div
|
||||
className={`${typeMapping.className} ${typeMapping.printable ? "note-detail-printable" : ""}`}
|
||||
style={{
|
||||
display: !isVisible ? "none" : "",
|
||||
height: isFullHeight ? "100%" : ""
|
||||
}}
|
||||
>
|
||||
{ <Element {...cachedProps} /> }
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -18,73 +18,117 @@ type NoteTypeView = () => Promise<{ default: TypeWidget } | TypeWidget> | ((prop
|
|||
|
||||
interface NoteTypeMapping {
|
||||
view: NoteTypeView;
|
||||
printable?: boolean;
|
||||
/** The class name to assign to the note type wrapper */
|
||||
className: string;
|
||||
}
|
||||
|
||||
export const TYPE_MAPPINGS: Record<ExtendedNoteType, NoteTypeMapping> = {
|
||||
empty: {
|
||||
view: () => import("./type_widgets/Empty"),
|
||||
className: "note-detail-empty",
|
||||
printable: true
|
||||
},
|
||||
doc: {
|
||||
view: () => import("./type_widgets/Doc")
|
||||
view: () => import("./type_widgets/Doc"),
|
||||
className: "note-detail-doc",
|
||||
printable: true
|
||||
},
|
||||
search: {
|
||||
view: () => <div className="note-detail-none note-detail-printable" />
|
||||
view: () => <></>,
|
||||
className: "note-detail-none",
|
||||
printable: true
|
||||
},
|
||||
protectedSession: {
|
||||
view: () => import("./type_widgets/ProtectedSession")
|
||||
view: () => import("./type_widgets/ProtectedSession"),
|
||||
className: "protected-session-password-component"
|
||||
},
|
||||
book: {
|
||||
view: () => import("./type_widgets/Book")
|
||||
view: () => import("./type_widgets/Book"),
|
||||
className: "note-detail-book",
|
||||
printable: true,
|
||||
},
|
||||
contentWidget: {
|
||||
view: () => import("./type_widgets/ContentWidget")
|
||||
view: () => import("./type_widgets/ContentWidget"),
|
||||
className: "note-detail-content-widget",
|
||||
printable: true
|
||||
},
|
||||
webView: {
|
||||
view: () => import("./type_widgets/WebView")
|
||||
view: () => import("./type_widgets/WebView"),
|
||||
className: "note-detail-web-view",
|
||||
printable: true
|
||||
},
|
||||
file: {
|
||||
view: () => import("./type_widgets/File")
|
||||
view: () => import("./type_widgets/File"),
|
||||
className: "note-detail-file",
|
||||
printable: true
|
||||
},
|
||||
image: {
|
||||
view: () => import("./type_widgets/Image")
|
||||
view: () => import("./type_widgets/Image"),
|
||||
className: "note-detail-image",
|
||||
printable: true
|
||||
},
|
||||
readOnlyCode: {
|
||||
view: async () => (await import("./type_widgets/code/Code")).ReadOnlyCode
|
||||
view: async () => (await import("./type_widgets/code/Code")).ReadOnlyCode,
|
||||
className: "note-detail-readonly-code",
|
||||
printable: true
|
||||
},
|
||||
editableCode: {
|
||||
view: async () => (await import("./type_widgets/code/Code")).EditableCode
|
||||
view: async () => (await import("./type_widgets/code/Code")).EditableCode,
|
||||
className: "note-detail-code",
|
||||
printable: true
|
||||
},
|
||||
mermaid: {
|
||||
view: () => import("./type_widgets/Mermaid")
|
||||
view: () => import("./type_widgets/Mermaid"),
|
||||
className: "note-detail-mermaid",
|
||||
printable: true
|
||||
},
|
||||
mindMap: {
|
||||
view: () => import("./type_widgets/MindMap")
|
||||
view: () => import("./type_widgets/MindMap"),
|
||||
className: "note-detail-mind-map",
|
||||
printable: true
|
||||
},
|
||||
attachmentList: {
|
||||
view: async () => (await import("./type_widgets/Attachment")).AttachmentList
|
||||
view: async () => (await import("./type_widgets/Attachment")).AttachmentList,
|
||||
className: "attachment-list",
|
||||
printable: true
|
||||
},
|
||||
attachmentDetail: {
|
||||
view: async () => (await import("./type_widgets/Attachment")).AttachmentDetail
|
||||
view: async () => (await import("./type_widgets/Attachment")).AttachmentDetail,
|
||||
className: "attachment-detail",
|
||||
printable: true
|
||||
},
|
||||
readOnlyText: {
|
||||
view: () => import("./type_widgets/text/ReadOnlyText")
|
||||
view: () => import("./type_widgets/text/ReadOnlyText"),
|
||||
className: "note-detail-readonly-text"
|
||||
},
|
||||
editableText: {
|
||||
view: () => import("./type_widgets/text/EditableText")
|
||||
view: () => import("./type_widgets/text/EditableText"),
|
||||
className: "note-detail-editable-text",
|
||||
printable: true
|
||||
},
|
||||
render: {
|
||||
view: () => import("./type_widgets/Render")
|
||||
view: () => import("./type_widgets/Render"),
|
||||
className: "note-detail-render",
|
||||
printable: true
|
||||
},
|
||||
canvas: {
|
||||
view: () => import("./type_widgets/Canvas")
|
||||
view: () => import("./type_widgets/Canvas"),
|
||||
className: "note-detail-canvas",
|
||||
printable: true
|
||||
},
|
||||
relationMap: {
|
||||
view: () => import("./type_widgets/relation_map/RelationMap")
|
||||
view: () => import("./type_widgets/relation_map/RelationMap"),
|
||||
className: "note-detail-relation-map",
|
||||
printable: true
|
||||
},
|
||||
noteMap: {
|
||||
view: () => import("./type_widgets/NoteMap")
|
||||
view: () => import("./type_widgets/NoteMap"),
|
||||
className: "note-detail-note-map",
|
||||
printable: true
|
||||
},
|
||||
aiChat: {
|
||||
view: () => import("./type_widgets/AiChat")
|
||||
view: () => import("./type_widgets/AiChat"),
|
||||
className: "ai-chat-widget-container"
|
||||
}
|
||||
};
|
||||
|
|
|
@ -31,7 +31,6 @@ export default function AiChat({ note, noteContext }: TypeWidgetProps) {
|
|||
return llmChatPanel;
|
||||
}, {
|
||||
noteContext,
|
||||
containerClassName: "ai-chat-widget-container",
|
||||
containerStyle: {
|
||||
height: "100%"
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ export function AttachmentList({ note }: TypeWidgetProps) {
|
|||
});
|
||||
|
||||
return (
|
||||
<div className="attachment-list note-detail-printable">
|
||||
<>
|
||||
<AttachmentListHeader noteId={note.noteId} />
|
||||
|
||||
<div className="attachment-list-wrapper">
|
||||
|
@ -58,7 +58,7 @@ export function AttachmentList({ note }: TypeWidgetProps) {
|
|||
</Alert>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,7 @@ export function AttachmentDetail({ note, viewScope }: TypeWidgetProps) {
|
|||
}, [ viewScope ]);
|
||||
|
||||
return (
|
||||
<div className="attachment-detail note-detail-printable">
|
||||
<>
|
||||
<div className="links-wrapper use-tn-links">
|
||||
{t("attachment_detail.owning_note")}{" "}
|
||||
<NoteLink notePath={note.noteId} />
|
||||
|
@ -122,7 +122,7 @@ export function AttachmentDetail({ note, viewScope }: TypeWidgetProps) {
|
|||
<strong>{t("attachment_detail.attachment_deleted")}</strong>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -23,13 +23,9 @@ export default function Book({ note }: TypeWidgetProps) {
|
|||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="note-detail-book note-detail-printable">
|
||||
{(shouldDisplayNoChildrenWarning && (
|
||||
<Alert type="warning" className="note-detail-book-empty-help">
|
||||
<RawHtml html={t("book.no_children_help")} />
|
||||
</Alert>
|
||||
))}
|
||||
</div>
|
||||
return (shouldDisplayNoChildrenWarning &&
|
||||
<Alert type="warning" className="note-detail-book-empty-help">
|
||||
<RawHtml html={t("book.no_children_help")} />
|
||||
</Alert>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -60,28 +60,26 @@ export default function Canvas({ note }: TypeWidgetProps) {
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<div className="canvas-widget note-detail-canvas note-detail-printable note-detail full-height" onWheel={onWheel}>
|
||||
<div className="canvas-render">
|
||||
<div className="excalidraw-wrapper">
|
||||
<Excalidraw
|
||||
excalidrawAPI={api => apiRef.current = api}
|
||||
theme={themeStyle}
|
||||
viewModeEnabled={isReadOnly || options.is("databaseReadonly")}
|
||||
zenModeEnabled={false}
|
||||
isCollaborating={false}
|
||||
detectScroll={false}
|
||||
handleKeyboardGlobally={false}
|
||||
autoFocus={false}
|
||||
UIOptions={{
|
||||
canvasActions: {
|
||||
saveToActiveFile: false,
|
||||
export: false
|
||||
}
|
||||
}}
|
||||
onLinkOpen={onLinkOpen}
|
||||
{...persistence}
|
||||
/>
|
||||
</div>
|
||||
<div className="canvas-render" onWheel={onWheel}>
|
||||
<div className="excalidraw-wrapper">
|
||||
<Excalidraw
|
||||
excalidrawAPI={api => apiRef.current = api}
|
||||
theme={themeStyle}
|
||||
viewModeEnabled={isReadOnly || options.is("databaseReadonly")}
|
||||
zenModeEnabled={false}
|
||||
isCollaborating={false}
|
||||
detectScroll={false}
|
||||
handleKeyboardGlobally={false}
|
||||
autoFocus={false}
|
||||
UIOptions={{
|
||||
canvasActions: {
|
||||
saveToActiveFile: false,
|
||||
export: false
|
||||
}
|
||||
}}
|
||||
onLinkOpen={onLinkOpen}
|
||||
{...persistence}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -50,12 +50,10 @@ const CONTENT_WIDGETS: Record<OptionPages | "_backendLog", (props: TypeWidgetPro
|
|||
export default function ContentWidget({ note, ...restProps }: TypeWidgetProps) {
|
||||
const Content = CONTENT_WIDGETS[note.noteId];
|
||||
return (
|
||||
<div className="note-detail-content-widget note-detail-printable">
|
||||
<div className={`note-detail-content-widget-content ${note.noteId.startsWith("_options") ? "options" : ""}`}>
|
||||
{Content
|
||||
? <Content note={note} {...restProps} />
|
||||
: (t("content_widget.unknown_widget", { id: note.noteId }))}
|
||||
</div>
|
||||
<div className={`note-detail-content-widget-content ${note.noteId.startsWith("_options") ? "options" : ""}`}>
|
||||
{Content
|
||||
? <Content note={note} {...restProps} />
|
||||
: (t("content_widget.unknown_widget", { id: note.noteId }))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -18,15 +18,15 @@
|
|||
border: 1px solid var(--main-border-color);
|
||||
}
|
||||
|
||||
.note-detail-doc.contextual-help {
|
||||
.note-detail-doc-content.contextual-help {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.note-detail-doc.contextual-help h2,
|
||||
.note-detail-doc.contextual-help h3,
|
||||
.note-detail-doc.contextual-help h4,
|
||||
.note-detail-doc.contextual-help h5,
|
||||
.note-detail-doc.contextual-help h6 {
|
||||
.note-detail-doc-content.contextual-help h2,
|
||||
.note-detail-doc-content.contextual-help h3,
|
||||
.note-detail-doc-content.contextual-help h4,
|
||||
.note-detail-doc-content.contextual-help h5,
|
||||
.note-detail-doc-content.contextual-help h6 {
|
||||
font-size: 1.25rem;
|
||||
background-color: var(--main-background-color);
|
||||
position: sticky;
|
||||
|
|
|
@ -27,12 +27,10 @@ export default function Doc({ note, viewScope, ntxId }: TypeWidgetProps) {
|
|||
});
|
||||
|
||||
return (
|
||||
<div className={`note-detail-doc note-detail-printable ${viewScope?.viewMode === "contextual-help" ? "contextual-help" : ""}`}>
|
||||
<RawHtmlBlock
|
||||
containerRef={containerRef}
|
||||
className="note-detail-doc-content ck-content"
|
||||
html={html}
|
||||
/>
|
||||
</div>
|
||||
<RawHtmlBlock
|
||||
containerRef={containerRef}
|
||||
className={`note-detail-doc-content ck-content ${viewScope?.viewMode === "contextual-help" ? "contextual-help" : ""}`}
|
||||
html={html}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -12,10 +12,10 @@ import { TypeWidgetProps } from "./type_widget";
|
|||
|
||||
export default function Empty({ }: TypeWidgetProps) {
|
||||
return (
|
||||
<div class="note-detail-empty note-detail-printable">
|
||||
<>
|
||||
<WorkspaceSwitcher />
|
||||
<NoteSearch />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -12,24 +12,17 @@ const TEXT_MAX_NUM_CHARS = 5000;
|
|||
export default function File({ note }: TypeWidgetProps) {
|
||||
const blob = useNoteBlob(note);
|
||||
|
||||
let preview: VNode | null = null;
|
||||
if (blob?.content) {
|
||||
preview = <TextPreview content={blob.content} />
|
||||
return <TextPreview content={blob.content} />
|
||||
} else if (note.mime === "application/pdf") {
|
||||
preview = <PdfPreview note={note} />
|
||||
return <PdfPreview note={note} />
|
||||
} else if (note.mime.startsWith("video/")) {
|
||||
preview = <VideoPreview note={note} />
|
||||
return <VideoPreview note={note} />
|
||||
} else if (note.mime.startsWith("audio/")) {
|
||||
preview = <AudioPreview note={note} />
|
||||
return <AudioPreview note={note} />
|
||||
} else {
|
||||
preview = <NoPreview />
|
||||
return <NoPreview />
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="note-detail-file note-detail-printable">
|
||||
{preview}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function TextPreview({ content }: { content: string }) {
|
||||
|
|
|
@ -41,14 +41,12 @@ export default function Image({ note, ntxId }: TypeWidgetProps) {
|
|||
});
|
||||
|
||||
return (
|
||||
<div className="note-detail-image note-detail-printable">
|
||||
<div ref={containerRef} className="note-detail-image-wrapper">
|
||||
<img
|
||||
id={uniqueId}
|
||||
className="note-detail-image-view"
|
||||
src={createImageSrcUrl(note)}
|
||||
/>
|
||||
</div>
|
||||
<div ref={containerRef} className="note-detail-image-wrapper">
|
||||
<img
|
||||
id={uniqueId}
|
||||
className="note-detail-image-view"
|
||||
src={createImageSrcUrl(note)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import nodeMenu from "@mind-elixir/node-menu";
|
|||
import "mind-elixir/style";
|
||||
import "@mind-elixir/node-menu/dist/style.css";
|
||||
import "./MindMap.css";
|
||||
import { useEditorSpacedUpdate, useNoteLabelBoolean, useTriliumEvent, useTriliumEvents, useTriliumOptionBool } from "../react/hooks";
|
||||
import { useEditorSpacedUpdate, useNoteLabelBoolean, useSyncedRef, useTriliumEvent, useTriliumEvents, useTriliumOptionBool } from "../react/hooks";
|
||||
import { refToJQuerySelector } from "../react/react_utils";
|
||||
import utils from "../../services/utils";
|
||||
|
||||
|
@ -16,6 +16,7 @@ const NEW_TOPIC_NAME = "";
|
|||
interface MindElixirProps {
|
||||
apiRef?: RefObject<MindElixirInstance>;
|
||||
containerProps?: Omit<HTMLAttributes<HTMLDivElement>, "ref">;
|
||||
containerRef?: RefObject<HTMLDivElement>;
|
||||
editable: boolean;
|
||||
content: MindElixirData;
|
||||
onChange?: () => void;
|
||||
|
@ -94,23 +95,22 @@ export default function MindMap({ note, ntxId }: TypeWidgetProps) {
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className="note-detail-mind-map note-detail-printable">
|
||||
<MindElixir
|
||||
apiRef={apiRef}
|
||||
content={content}
|
||||
onChange={() => spacedUpdate.scheduleUpdate()}
|
||||
editable={!isReadOnly}
|
||||
containerProps={{
|
||||
className: "mind-map-container",
|
||||
onKeyDown
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<MindElixir
|
||||
containerRef={containerRef}
|
||||
apiRef={apiRef}
|
||||
content={content}
|
||||
onChange={() => spacedUpdate.scheduleUpdate()}
|
||||
editable={!isReadOnly}
|
||||
containerProps={{
|
||||
className: "mind-map-container",
|
||||
onKeyDown
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function MindElixir({ content, containerProps, apiRef: externalApiRef, onChange, editable }: MindElixirProps) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
function MindElixir({ content, containerRef: externalContainerRef, containerProps, apiRef: externalApiRef, onChange, editable }: MindElixirProps) {
|
||||
const containerRef = useSyncedRef<HTMLDivElement>(externalContainerRef, null);
|
||||
const apiRef = useRef<MindElixirInstance>(null);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -6,7 +6,7 @@ export default function NoteMap({ note }: TypeWidgetProps) {
|
|||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className="note-detail-note-map note-detail-printable">
|
||||
<div ref={containerRef}>
|
||||
<NoteMapEl parentRef={containerRef} note={note} widgetMode="type" />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -20,23 +20,21 @@ export default function ProtectedSession() {
|
|||
}, [ passwordRef ]);
|
||||
|
||||
return (
|
||||
<div className="protected-session-password-component note-detail-printable">
|
||||
<form class="protected-session-password-form" onSubmit={submitCallback}>
|
||||
<FormGroup name="protected-session-password-in-detail" label={t("protected_session.enter_password_instruction")}>
|
||||
<FormTextBox
|
||||
type="password"
|
||||
className="protected-session-password"
|
||||
autocomplete="current-password"
|
||||
inputRef={passwordRef}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<Button
|
||||
text={t("protected_session.start_session_button")}
|
||||
primary
|
||||
keyboardShortcut="Enter"
|
||||
<form class="protected-session-password-form" onSubmit={submitCallback}>
|
||||
<FormGroup name="protected-session-password-in-detail" label={t("protected_session.enter_password_instruction")}>
|
||||
<FormTextBox
|
||||
type="password"
|
||||
className="protected-session-password"
|
||||
autocomplete="current-password"
|
||||
inputRef={passwordRef}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</FormGroup>
|
||||
|
||||
<Button
|
||||
text={t("protected_session.start_session_button")}
|
||||
primary
|
||||
keyboardShortcut="Enter"
|
||||
/>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ export default function Render({ note, noteContext, ntxId }: TypeWidgetProps) {
|
|||
});
|
||||
|
||||
return (
|
||||
<div className="note-detail-render note-detail-printable">
|
||||
<>
|
||||
{!renderNotesFound && (
|
||||
<Alert className="note-detail-render-help" type="warning">
|
||||
<p><strong>{t("render.note_detail_render_help_1")}</strong></p>
|
||||
|
@ -47,6 +47,6 @@ export default function Render({ note, noteContext, ntxId }: TypeWidgetProps) {
|
|||
)}
|
||||
|
||||
<div ref={contentRef} className="note-detail-render-content" />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -10,13 +10,10 @@ const isElectron = utils.isElectron();
|
|||
export default function WebView({ note }: TypeWidgetProps) {
|
||||
const [ webViewSrc ] = useNoteLabel(note, "webViewSrc");
|
||||
|
||||
return (
|
||||
<div className="note-detail-web-view note-detail-printable">
|
||||
{webViewSrc
|
||||
? <WebViewContent src={webViewSrc} />
|
||||
: <WebViewHelp />}
|
||||
</div>
|
||||
)
|
||||
return (webViewSrc
|
||||
? <WebViewContent src={webViewSrc} />
|
||||
: <WebViewHelp />
|
||||
);
|
||||
}
|
||||
|
||||
function WebViewContent({ src }: { src: string }) {
|
||||
|
|
|
@ -44,15 +44,13 @@ export function ReadOnlyCode({ note, viewScope, ntxId, parentComponent }: TypeWi
|
|||
}, [ blob ]);
|
||||
|
||||
return (
|
||||
<div className="note-detail-readonly-code note-detail-printable">
|
||||
<CodeEditor
|
||||
ntxId={ntxId} parentComponent={parentComponent}
|
||||
className="note-detail-readonly-code-content"
|
||||
content={content}
|
||||
mime={note.mime}
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
<CodeEditor
|
||||
ntxId={ntxId} parentComponent={parentComponent}
|
||||
className="note-detail-readonly-code-content"
|
||||
content={content}
|
||||
mime={note.mime}
|
||||
readOnly
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -93,7 +91,7 @@ export function EditableCode({ note, ntxId, debounceUpdate, parentComponent, upd
|
|||
useKeyboardShortcuts("code-detail", containerRef, parentComponent);
|
||||
|
||||
return (
|
||||
<div className="note-detail-code note-detail-printable">
|
||||
<>
|
||||
<CodeEditor
|
||||
ntxId={ntxId} parentComponent={parentComponent}
|
||||
editorRef={editorRef} containerRef={containerRef}
|
||||
|
@ -119,7 +117,7 @@ export function EditableCode({ note, ntxId, debounceUpdate, parentComponent, upd
|
|||
<TouchBarButton icon="NSImageNameTouchBarPlayTemplate" click={() => appContext.triggerCommand("runActiveNote")} />
|
||||
)}
|
||||
</TouchBar>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -133,30 +133,28 @@ export default function RelationMap({ note, ntxId }: TypeWidgetProps) {
|
|||
useRelationData(note.noteId, data, mapApiRef, pbApiRef);
|
||||
|
||||
return (
|
||||
<div className="note-detail-relation-map note-detail-printable">
|
||||
<div
|
||||
className="relation-map-wrapper"
|
||||
onClick={clickCallback}
|
||||
{...dragProps}
|
||||
<div
|
||||
className="relation-map-wrapper"
|
||||
onClick={clickCallback}
|
||||
{...dragProps}
|
||||
>
|
||||
<JsPlumb
|
||||
apiRef={pbApiRef}
|
||||
containerRef={containerRef}
|
||||
className="relation-map-container"
|
||||
props={{
|
||||
Endpoint: ["Dot", { radius: 2 }],
|
||||
Connector: "StateMachine",
|
||||
ConnectionOverlays: uniDirectionalOverlays,
|
||||
HoverPaintStyle: { stroke: "#777", strokeWidth: 1 },
|
||||
}}
|
||||
onInstanceCreated={setupOverlays}
|
||||
onConnection={connectionCallback}
|
||||
>
|
||||
<JsPlumb
|
||||
apiRef={pbApiRef}
|
||||
containerRef={containerRef}
|
||||
className="relation-map-container"
|
||||
props={{
|
||||
Endpoint: ["Dot", { radius: 2 }],
|
||||
Connector: "StateMachine",
|
||||
ConnectionOverlays: uniDirectionalOverlays,
|
||||
HoverPaintStyle: { stroke: "#777", strokeWidth: 1 },
|
||||
}}
|
||||
onInstanceCreated={setupOverlays}
|
||||
onConnection={connectionCallback}
|
||||
>
|
||||
{data?.notes.map(note => (
|
||||
<NoteBox {...note} mapApiRef={mapApiRef} />
|
||||
))}
|
||||
</JsPlumb>
|
||||
</div>
|
||||
{data?.notes.map(note => (
|
||||
<NoteBox {...note} mapApiRef={mapApiRef} />
|
||||
))}
|
||||
</JsPlumb>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { HTMLProps, RefObject, useEffect, useImperativeHandle, useRef, useState } from "preact/compat";
|
||||
import { PopupEditor, ClassicEditor, EditorWatchdog, type WatchdogConfig, CKTextEditor, TemplateDefinition } from "@triliumnext/ckeditor5";
|
||||
import { buildConfig, BuildEditorOptions } from "./config";
|
||||
import { useLegacyImperativeHandlers } from "../../react/hooks";
|
||||
import { useLegacyImperativeHandlers, useSyncedRef } from "../../react/hooks";
|
||||
import link from "../../../services/link";
|
||||
import froca from "../../../services/froca";
|
||||
|
||||
|
@ -30,10 +30,11 @@ interface CKEditorWithWatchdogProps extends Pick<HTMLProps<HTMLDivElement>, "cla
|
|||
onEditorInitialized?: (editor: CKTextEditor) => void;
|
||||
editorApi: RefObject<CKEditorApi>;
|
||||
templates: TemplateDefinition[];
|
||||
containerRef?: RefObject<HTMLDivElement>;
|
||||
}
|
||||
|
||||
export default function CKEditorWithWatchdog({ content, contentLanguage, className, tabIndex, isClassicEditor, watchdogRef: externalWatchdogRef, watchdogConfig, onNotificationWarning, onWatchdogStateChange, onChange, onEditorInitialized, editorApi, templates }: CKEditorWithWatchdogProps) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
export default function CKEditorWithWatchdog({ containerRef: externalContainerRef, content, contentLanguage, className, tabIndex, isClassicEditor, watchdogRef: externalWatchdogRef, watchdogConfig, onNotificationWarning, onWatchdogStateChange, onChange, onEditorInitialized, editorApi, templates }: CKEditorWithWatchdogProps) {
|
||||
const containerRef = useSyncedRef<HTMLDivElement>(externalContainerRef, null);
|
||||
const watchdogRef = useRef<EditorWatchdog>(null);
|
||||
const [ editor, setEditor ] = useState<CKTextEditor>();
|
||||
|
||||
|
|
|
@ -185,9 +185,10 @@ export default function EditableText({ note, parentComponent, ntxId, noteContext
|
|||
});
|
||||
|
||||
return (
|
||||
<div ref={containerRef} class={`note-detail-editable-text note-detail-printable ${codeBlockWordWrap ? "word-wrap" : ""}`}>
|
||||
<>
|
||||
{note && !!templates && <CKEditorWithWatchdog
|
||||
className="note-detail-editable-text-editor use-tn-links"
|
||||
containerRef={containerRef}
|
||||
className={`note-detail-editable-text-editor use-tn-links ${codeBlockWordWrap ? "word-wrap" : ""}`}
|
||||
tabIndex={300}
|
||||
content={content}
|
||||
contentLanguage={language}
|
||||
|
@ -229,7 +230,7 @@ export default function EditableText({ note, parentComponent, ntxId, noteContext
|
|||
/>}
|
||||
|
||||
<EditableTextTouchBar watchdogRef={watchdogRef} refreshTouchBarRef={refreshTouchBarRef} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue