chore(react/type_widget): port doc widget

This commit is contained in:
Elian Doran
2025-09-19 18:32:45 +03:00
parent 07b86c8cf7
commit bbb927c83f
5 changed files with 108 additions and 97 deletions

View File

@@ -6,6 +6,8 @@ import { useEffect, useState } from "preact/hooks";
import NoteContext from "../components/note_context";
import Empty from "./type_widgets/Empty";
import { VNode } from "preact";
import Doc from "./type_widgets/Doc";
import { TypeWidgetProps } from "./type_widgets/type_widget";
/**
* A `NoteType` altered by the note detail widget, taking into consideration whether the note is editable or not and adding special note types such as an empty one,
@@ -18,9 +20,15 @@ type ExtendedNoteType = Exclude<NoteType, "launcher" | "text" | "code"> | "empty
*/
export default function NoteDetail() {
const { note, type } = useNoteInfo();
const { viewScope, ntxId } = useNoteContext();
const [ correspondingWidget, setCorrespondingWidget ] = useState<VNode>();
useEffect(() => setCorrespondingWidget(getCorrespondingWidget(type)), [ type ]);
const props: TypeWidgetProps = {
note: note!,
viewScope,
ntxId
};
useEffect(() => setCorrespondingWidget(getCorrespondingWidget(type, props)), [ note, viewScope, type ]);
return (
<div>
@@ -45,10 +53,12 @@ function useNoteInfo() {
return { note, type };
}
function getCorrespondingWidget(noteType: ExtendedNoteType | undefined) {
function getCorrespondingWidget(noteType: ExtendedNoteType | undefined, props: TypeWidgetProps) {
switch (noteType) {
case "empty":
return <Empty />
case "doc":
return <Doc {...props} />
default:
break;
}

View File

@@ -0,0 +1,50 @@
.note-detail-doc-content {
padding: 15px;
}
.note-detail-doc-content pre {
border: 0;
box-shadow: var(--code-block-box-shadow);
padding: 15px;
border-radius: 5px;
}
.note-detail-doc-content code {
font-variant: none;
}
.note-detail-doc-content pre:not(.hljs) {
background-color: var(--accented-background-color);
border: 1px solid var(--main-border-color);
}
.note-detail-doc.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 {
font-size: 1.25rem;
background-color: var(--main-background-color);
position: sticky;
top: 0;
z-index: 50;
margin: 0;
padding-bottom: 0.25em;
}
img {
max-width: 100%;
height: auto;
}
td img {
max-width: 40vw;
}
figure.table {
overflow: auto !important;
}

View File

@@ -0,0 +1,38 @@
import { useEffect, useRef, useState } from "preact/hooks";
import { RawHtmlBlock } from "../react/RawHtml";
import renderDoc from "../../services/doc_renderer";
import "./Doc.css";
import { TypeWidgetProps } from "./type_widget";
import { useTriliumEvent } from "../react/hooks";
import { refToJQuerySelector } from "../react/react_utils";
export default function Doc({ note, viewScope, ntxId }: TypeWidgetProps) {
const [ html, setHtml ] = useState<string>();
const initialized = useRef<Promise<void> | null>(null);
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!note) return;
initialized.current = renderDoc(note).then($content => {
setHtml($content.html());
});
}, [ note ]);
useTriliumEvent("executeWithContentElement", async ({ resolve, ntxId: eventNtxId}) => {
console.log("Got request for content ", ntxId, eventNtxId);
if (eventNtxId !== ntxId) return;
await initialized.current;
resolve(refToJQuerySelector(containerRef));
});
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>
);
}

View File

@@ -0,0 +1,8 @@
import FNote from "../../entities/fnote";
import { ViewScope } from "../../services/link";
export interface TypeWidgetProps {
note: FNote;
viewScope: ViewScope | undefined;
ntxId: string | null | undefined;
}

View File

@@ -1,95 +0,0 @@
import type { EventData } from "../../components/app_context.js";
import type FNote from "../../entities/fnote.js";
import renderDoc from "../../services/doc_renderer.js";
import TypeWidget from "./type_widget.js";
const TPL = /*html*/`<div class="note-detail-doc note-detail-printable">
<style>
.note-detail-doc-content {
padding: 15px;
}
.note-detail-doc-content pre {
border: 0;
box-shadow: var(--code-block-box-shadow);
padding: 15px;
border-radius: 5px;
}
.note-detail-doc-content code {
font-variant: none;
}
.note-detail-doc-content pre:not(.hljs) {
background-color: var(--accented-background-color);
border: 1px solid var(--main-border-color);
}
.note-detail-doc.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 {
font-size: 1.25rem;
background-color: var(--main-background-color);
position: sticky;
top: 0;
z-index: 50;
margin: 0;
padding-bottom: 0.25em;
}
img {
max-width: 100%;
height: auto;
}
td img {
max-width: 40vw;
}
figure.table {
overflow: auto !important;
}
</style>
<div class="note-detail-doc-content ck-content"></div>
</div>`;
export default class DocTypeWidget extends TypeWidget {
private $content!: JQuery<HTMLElement>;
static getType() {
return "doc";
}
doRender() {
this.$widget = $(TPL);
this.$content = this.$widget.find(".note-detail-doc-content");
super.doRender();
}
async doRefresh(note: FNote) {
this.initialized = renderDoc(note).then(($content) => {
this.$content.html($content.html());
});
this.$widget.toggleClass("contextual-help", this.noteContext?.viewScope?.viewMode === "contextual-help");
}
async executeWithContentElementEvent({ resolve, ntxId }: EventData<"executeWithContentElement">) {
if (!this.isNoteContext(ntxId)) {
return;
}
await this.initialized;
resolve(this.$content);
}
}