mirror of
https://github.com/zadam/trilium.git
synced 2025-11-10 23:35:50 +01:00
chore(react/type_widget): port content widget
This commit is contained in:
@@ -10,6 +10,7 @@ import Doc from "./type_widgets/Doc";
|
|||||||
import { TypeWidgetProps } from "./type_widgets/type_widget";
|
import { TypeWidgetProps } from "./type_widgets/type_widget";
|
||||||
import ProtectedSession from "./type_widgets/ProtectedSession";
|
import ProtectedSession from "./type_widgets/ProtectedSession";
|
||||||
import Book from "./type_widgets/Book";
|
import Book from "./type_widgets/Book";
|
||||||
|
import ContentWidget from "./type_widgets/ContentWidget";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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,
|
* 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,
|
||||||
@@ -62,6 +63,7 @@ function getCorrespondingWidget(noteType: ExtendedNoteType | undefined, props: T
|
|||||||
case "search": return <div className="note-detail-none note-detail-printable" />
|
case "search": return <div className="note-detail-none note-detail-printable" />
|
||||||
case "protectedSession": return <ProtectedSession />
|
case "protectedSession": return <ProtectedSession />
|
||||||
case "book": return <Book {...props} />
|
case "book": return <Book {...props} />
|
||||||
|
case "contentWidget": return <ContentWidget {...props} />
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ import sync from "../../services/sync";
|
|||||||
import HelpButton from "../react/HelpButton";
|
import HelpButton from "../react/HelpButton";
|
||||||
import { TabContext } from "./ribbon-interface";
|
import { TabContext } from "./ribbon-interface";
|
||||||
import Modal from "../react/Modal";
|
import Modal from "../react/Modal";
|
||||||
import { CodeMimeTypesList } from "../type_widgets_old/options/code_notes";
|
import { CodeMimeTypesList } from "../type_widgets/options/code_notes";
|
||||||
import { ContentLanguagesList } from "../type_widgets_old/options/i18n";
|
import { ContentLanguagesList } from "../type_widgets/options/i18n";
|
||||||
|
|
||||||
export default function BasicPropertiesTab({ note }: TabContext) {
|
export default function BasicPropertiesTab({ note }: TabContext) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
16
apps/client/src/widgets/type_widgets/ContentWidget.css
Normal file
16
apps/client/src/widgets/type_widgets/ContentWidget.css
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
.type-contentWidget .note-detail {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-detail-content-widget {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-detail-content-widget-content {
|
||||||
|
padding: 15px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-detail.full-height .note-detail-content-widget-content {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
60
apps/client/src/widgets/type_widgets/ContentWidget.tsx
Normal file
60
apps/client/src/widgets/type_widgets/ContentWidget.tsx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { TypeWidgetProps } from "./type_widget";
|
||||||
|
import { JSX } from "preact/jsx-runtime";
|
||||||
|
import { OptionPages } from "../type_widgets_old/content_widget";
|
||||||
|
import AppearanceSettings from "./options/appearance";
|
||||||
|
import ShortcutSettings from "./options/shortcuts";
|
||||||
|
import TextNoteSettings from "./options/text_notes";
|
||||||
|
import CodeNoteSettings from "./options/code_notes";
|
||||||
|
import ImageSettings from "./options/images";
|
||||||
|
import SpellcheckSettings from "./options/spellcheck";
|
||||||
|
import PasswordSettings from "./options/password";
|
||||||
|
import MultiFactorAuthenticationSettings from "./options/multi_factor_authentication";
|
||||||
|
import EtapiSettings from "./options/etapi";
|
||||||
|
import BackupSettings from "./options/backup";
|
||||||
|
import SyncOptions from "./options/sync";
|
||||||
|
import AiSettings from "./options/ai_settings";
|
||||||
|
import OtherSettings from "./options/other";
|
||||||
|
import InternationalizationOptions from "./options/i18n";
|
||||||
|
import AdvancedSettings from "./options/advanced";
|
||||||
|
import "./ContentWidget.css";
|
||||||
|
import { t } from "../../services/i18n";
|
||||||
|
|
||||||
|
export type OptionPages = "_optionsAppearance" | "_optionsShortcuts" | "_optionsTextNotes" | "_optionsCodeNotes" | "_optionsImages" | "_optionsSpellcheck" | "_optionsPassword" | "_optionsMFA" | "_optionsEtapi" | "_optionsBackup" | "_optionsSync" | "_optionsAi" | "_optionsOther" | "_optionsLocalization" | "_optionsAdvanced";
|
||||||
|
|
||||||
|
const CONTENT_WIDGETS: Record<OptionPages | "_backendLog", () => JSX.Element> = {
|
||||||
|
_optionsAppearance: AppearanceSettings,
|
||||||
|
_optionsShortcuts: ShortcutSettings,
|
||||||
|
_optionsTextNotes: TextNoteSettings,
|
||||||
|
_optionsCodeNotes: CodeNoteSettings,
|
||||||
|
_optionsImages: ImageSettings,
|
||||||
|
_optionsSpellcheck: SpellcheckSettings,
|
||||||
|
_optionsPassword: PasswordSettings,
|
||||||
|
_optionsMFA: MultiFactorAuthenticationSettings,
|
||||||
|
_optionsEtapi: EtapiSettings,
|
||||||
|
_optionsBackup: BackupSettings,
|
||||||
|
_optionsSync: SyncOptions,
|
||||||
|
_optionsAi: AiSettings,
|
||||||
|
_optionsOther: OtherSettings,
|
||||||
|
_optionsLocalization: InternationalizationOptions,
|
||||||
|
_optionsAdvanced: AdvancedSettings,
|
||||||
|
_backendLog: () => <></> // FIXME
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type widget that displays one or more widgets based on the type of note, generally used for options and other interactive notes such as the backend log.
|
||||||
|
*
|
||||||
|
* @param param0
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export default function ContentWidget({ note }: 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 />
|
||||||
|
: (t("content_widget.unknown_widget", { id: note.noteId }))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
1
apps/client/src/widgets/type_widgets/constants.ts
Normal file
1
apps/client/src/widgets/type_widgets/constants.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const CODE_THEME_DEFAULT_PREFIX = "default:";
|
||||||
@@ -8,7 +8,7 @@ import { useTriliumOption, useTriliumOptionBool, useTriliumOptionJson } from "..
|
|||||||
import OptionsSection from "./components/OptionsSection";
|
import OptionsSection from "./components/OptionsSection";
|
||||||
import { useEffect, useMemo, useRef } from "preact/hooks";
|
import { useEffect, useMemo, useRef } from "preact/hooks";
|
||||||
import codeNoteSample from "./samples/code_note.txt?raw";
|
import codeNoteSample from "./samples/code_note.txt?raw";
|
||||||
import { DEFAULT_PREFIX } from "../abstract_code_type_widget";
|
import { CODE_THEME_DEFAULT_PREFIX as DEFAULT_PREFIX } from "../constants";
|
||||||
import { MimeType } from "@triliumnext/commons";
|
import { MimeType } from "@triliumnext/commons";
|
||||||
import mime_types from "../../../services/mime_types";
|
import mime_types from "../../../services/mime_types";
|
||||||
import CheckboxList from "./components/CheckboxList";
|
import CheckboxList from "./components/CheckboxList";
|
||||||
@@ -58,7 +58,7 @@ function Appearance() {
|
|||||||
<OptionsSection title={t("code_theme.title")}>
|
<OptionsSection title={t("code_theme.title")}>
|
||||||
<div className="row" style={{ marginBottom: "15px" }}>
|
<div className="row" style={{ marginBottom: "15px" }}>
|
||||||
<FormGroup name="color-scheme" label={t("code_theme.color-scheme")} className="col-md-6" style={{ marginBottom: 0 }}>
|
<FormGroup name="color-scheme" label={t("code_theme.color-scheme")} className="col-md-6" style={{ marginBottom: 0 }}>
|
||||||
<FormSelect
|
<FormSelect
|
||||||
values={themes}
|
values={themes}
|
||||||
keyProperty="id" titleProperty="name"
|
keyProperty="id" titleProperty="name"
|
||||||
currentValue={codeNoteTheme} onChange={setCodeNoteTheme}
|
currentValue={codeNoteTheme} onChange={setCodeNoteTheme}
|
||||||
@@ -148,7 +148,7 @@ export function CodeMimeTypesList() {
|
|||||||
plainTextMimeType.enabled = true;
|
plainTextMimeType.enabled = true;
|
||||||
plainTextMimeType.disabled = true;
|
plainTextMimeType.disabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const mimeType of ungroupedMimeTypes) {
|
for (const mimeType of ungroupedMimeTypes) {
|
||||||
const initial = mimeType.title.charAt(0).toUpperCase();
|
const initial = mimeType.title.charAt(0).toUpperCase();
|
||||||
if (!result[initial]) {
|
if (!result[initial]) {
|
||||||
@@ -157,7 +157,7 @@ export function CodeMimeTypesList() {
|
|||||||
result[initial].push(mimeType);
|
result[initial].push(mimeType);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}, [ codeNotesMimeTypes ]);
|
}, [ codeNotesMimeTypes ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ul class="options-mime-types">
|
<ul class="options-mime-types">
|
||||||
@@ -174,4 +174,4 @@ export function CodeMimeTypesList() {
|
|||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import OptionsSection from "./OptionsSection";
|
import OptionsSection from "./OptionsSection";
|
||||||
import type { OptionPages } from "../../content_widget";
|
import type { OptionPages } from "../../ContentWidget";
|
||||||
import { t } from "../../../../services/i18n";
|
import { t } from "../../../../services/i18n";
|
||||||
|
|
||||||
interface RelatedSettingsProps {
|
interface RelatedSettingsProps {
|
||||||
@@ -21,4 +21,4 @@ export default function RelatedSettings({ items }: RelatedSettingsProps) {
|
|||||||
</nav>
|
</nav>
|
||||||
</OptionsSection>
|
</OptionsSection>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,6 @@ import TypeWidget from "./type_widget.js";
|
|||||||
import CodeMirror, { type EditorConfig } from "@triliumnext/codemirror";
|
import CodeMirror, { type EditorConfig } from "@triliumnext/codemirror";
|
||||||
import type { EventData } from "../../components/app_context.js";
|
import type { EventData } from "../../components/app_context.js";
|
||||||
|
|
||||||
export const DEFAULT_PREFIX = "default:";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract {@link TypeWidget} which implements the CodeMirror editor, meant to be used as a parent for
|
* An abstract {@link TypeWidget} which implements the CodeMirror editor, meant to be used as a parent for
|
||||||
|
|||||||
@@ -1,136 +0,0 @@
|
|||||||
import TypeWidget from "./type_widget.js";
|
|
||||||
import type FNote from "../../entities/fnote.js";
|
|
||||||
import type NoteContextAwareWidget from "../note_context_aware_widget.js";
|
|
||||||
import { t } from "../../services/i18n.js";
|
|
||||||
import type { JSX } from "preact/jsx-runtime";
|
|
||||||
import AppearanceSettings from "./options/appearance.jsx";
|
|
||||||
import { disposeReactWidget, renderReactWidgetAtElement } from "../react/react_utils.jsx";
|
|
||||||
import ImageSettings from "./options/images.jsx";
|
|
||||||
import AdvancedSettings from "./options/advanced.jsx";
|
|
||||||
import InternationalizationOptions from "./options/i18n.jsx";
|
|
||||||
import SyncOptions from "./options/sync.jsx";
|
|
||||||
import EtapiSettings from "./options/etapi.js";
|
|
||||||
import BackupSettings from "./options/backup.js";
|
|
||||||
import SpellcheckSettings from "./options/spellcheck.js";
|
|
||||||
import PasswordSettings from "./options/password.jsx";
|
|
||||||
import ShortcutSettings from "./options/shortcuts.js";
|
|
||||||
import TextNoteSettings from "./options/text_notes.jsx";
|
|
||||||
import CodeNoteSettings from "./options/code_notes.jsx";
|
|
||||||
import OtherSettings from "./options/other.jsx";
|
|
||||||
import BackendLogWidget from "./content/backend_log.js";
|
|
||||||
import MultiFactorAuthenticationSettings from "./options/multi_factor_authentication.js";
|
|
||||||
import AiSettings from "./options/ai_settings.jsx";
|
|
||||||
import { unmountComponentAtNode } from "preact/compat";
|
|
||||||
|
|
||||||
const TPL = /*html*/`<div class="note-detail-content-widget note-detail-printable">
|
|
||||||
<style>
|
|
||||||
.type-contentWidget .note-detail {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-detail-content-widget {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-detail-content-widget-content {
|
|
||||||
padding: 15px;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-detail.full-height .note-detail-content-widget-content {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="note-detail-content-widget-content"></div>
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
export type OptionPages = "_optionsAppearance" | "_optionsShortcuts" | "_optionsTextNotes" | "_optionsCodeNotes" | "_optionsImages" | "_optionsSpellcheck" | "_optionsPassword" | "_optionsMFA" | "_optionsEtapi" | "_optionsBackup" | "_optionsSync" | "_optionsAi" | "_optionsOther" | "_optionsLocalization" | "_optionsAdvanced";
|
|
||||||
|
|
||||||
const CONTENT_WIDGETS: Record<OptionPages | "_backendLog", ((typeof NoteContextAwareWidget)[] | JSX.Element)> = {
|
|
||||||
_optionsAppearance: <AppearanceSettings />,
|
|
||||||
_optionsShortcuts: <ShortcutSettings />,
|
|
||||||
_optionsTextNotes: <TextNoteSettings />,
|
|
||||||
_optionsCodeNotes: <CodeNoteSettings />,
|
|
||||||
_optionsImages: <ImageSettings />,
|
|
||||||
_optionsSpellcheck: <SpellcheckSettings />,
|
|
||||||
_optionsPassword: <PasswordSettings />,
|
|
||||||
_optionsMFA: <MultiFactorAuthenticationSettings />,
|
|
||||||
_optionsEtapi: <EtapiSettings />,
|
|
||||||
_optionsBackup: <BackupSettings />,
|
|
||||||
_optionsSync: <SyncOptions />,
|
|
||||||
_optionsAi: <AiSettings />,
|
|
||||||
_optionsOther: <OtherSettings />,
|
|
||||||
_optionsLocalization: <InternationalizationOptions />,
|
|
||||||
_optionsAdvanced: <AdvancedSettings />,
|
|
||||||
_backendLog: [
|
|
||||||
BackendLogWidget
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type widget that displays one or more widgets based on the type of note, generally used for options and other interactive notes such as the backend log.
|
|
||||||
*
|
|
||||||
* One important aspect is that, like its parent {@link TypeWidget}, the content widgets don't receive all events by default and they must be manually added
|
|
||||||
* to the propagation list in {@link TypeWidget.handleEventInChildren}.
|
|
||||||
*/
|
|
||||||
export default class ContentWidgetTypeWidget extends TypeWidget {
|
|
||||||
private $content!: JQuery<HTMLElement>;
|
|
||||||
|
|
||||||
static getType() {
|
|
||||||
return "contentWidget";
|
|
||||||
}
|
|
||||||
|
|
||||||
doRender() {
|
|
||||||
this.$widget = $(TPL);
|
|
||||||
this.$content = this.$widget.find(".note-detail-content-widget-content");
|
|
||||||
|
|
||||||
super.doRender();
|
|
||||||
}
|
|
||||||
|
|
||||||
async doRefresh(note: FNote) {
|
|
||||||
unmountComponentAtNode(this.$content[0]);
|
|
||||||
this.$content.empty();
|
|
||||||
this.children = [];
|
|
||||||
|
|
||||||
const contentWidgets = (CONTENT_WIDGETS as Record<string, (typeof NoteContextAwareWidget[] | JSX.Element)>)[note.noteId];
|
|
||||||
this.$content.toggleClass("options", note.noteId.startsWith("_options"));
|
|
||||||
|
|
||||||
// Unknown widget.
|
|
||||||
if (!contentWidgets) {
|
|
||||||
this.$content.append(t("content_widget.unknown_widget", { id: note.noteId }));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Legacy widget.
|
|
||||||
if (Array.isArray(contentWidgets)) {
|
|
||||||
for (const clazz of contentWidgets) {
|
|
||||||
const widget = new clazz();
|
|
||||||
|
|
||||||
if (this.noteContext) {
|
|
||||||
await widget.handleEvent("setNoteContext", { noteContext: this.noteContext });
|
|
||||||
}
|
|
||||||
this.child(widget);
|
|
||||||
|
|
||||||
this.$content.append(widget.render());
|
|
||||||
await widget.refresh();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// React widget.
|
|
||||||
renderReactWidgetAtElement(this, contentWidgets, this.$content[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup(): void {
|
|
||||||
if (this.noteId) {
|
|
||||||
const contentWidgets = (CONTENT_WIDGETS as Record<string, (typeof NoteContextAwareWidget[] | JSX.Element)>)[this.noteId];
|
|
||||||
if (contentWidgets && !Array.isArray(contentWidgets)) {
|
|
||||||
disposeReactWidget(this.$content[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
super.cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user