diff --git a/apps/client/src/widgets/react/Column.tsx b/apps/client/src/widgets/react/Column.tsx
new file mode 100644
index 000000000..682d750d6
--- /dev/null
+++ b/apps/client/src/widgets/react/Column.tsx
@@ -0,0 +1,14 @@
+import type { ComponentChildren } from "preact";
+
+interface ColumnProps {
+ md: number;
+ children: ComponentChildren;
+}
+
+export default function Column({ md, children }: ColumnProps) {
+ return (
+
+ {children}
+
+ )
+}
\ No newline at end of file
diff --git a/apps/client/src/widgets/react/FormSelect.tsx b/apps/client/src/widgets/react/FormSelect.tsx
new file mode 100644
index 000000000..1a13d1571
--- /dev/null
+++ b/apps/client/src/widgets/react/FormSelect.tsx
@@ -0,0 +1,25 @@
+interface FormSelectProps {
+ currentValue?: string;
+ onChange(newValue: string): void;
+ values: { val: string, title: string }[];
+}
+
+export default function FormSelect({ currentValue, values, onChange }: FormSelectProps) {
+ return (
+
+ )
+}
\ No newline at end of file
diff --git a/apps/client/src/widgets/react/ReactBasicWidget.tsx b/apps/client/src/widgets/react/ReactBasicWidget.tsx
index 18e9b87ec..f25a3b71e 100644
--- a/apps/client/src/widgets/react/ReactBasicWidget.tsx
+++ b/apps/client/src/widgets/react/ReactBasicWidget.tsx
@@ -28,5 +28,5 @@ export function renderReactWidget(parentComponent: Component, el: JSX.Element) {
{el}
), renderContainer);
- return $(renderContainer.firstChild as HTMLElement);
+ return $(renderContainer.children) as JQuery;
}
\ No newline at end of file
diff --git a/apps/client/src/widgets/react/hooks.tsx b/apps/client/src/widgets/react/hooks.tsx
index c275f5a40..a99fa45dc 100644
--- a/apps/client/src/widgets/react/hooks.tsx
+++ b/apps/client/src/widgets/react/hooks.tsx
@@ -4,7 +4,7 @@ import { ParentComponent } from "./ReactBasicWidget";
import SpacedUpdate from "../../services/spaced_update";
import { OptionNames } from "@triliumnext/commons";
import options from "../../services/options";
-import utils from "../../services/utils";
+import utils, { reloadFrontendApp } from "../../services/utils";
/**
* Allows a React component to react to Trilium events (e.g. `entitiesReloaded`). When the desired event is triggered, the handler is invoked with the event parameters.
@@ -68,12 +68,16 @@ export function useSpacedUpdate(callback: () => Promise, interval = 1000)
return spacedUpdateRef.current;
}
-export function useTriliumOption(name: OptionNames): [string, (newValue: string) => Promise] {
+export function useTriliumOption(name: OptionNames, needsRefresh?: boolean): [string, (newValue: string) => Promise] {
const initialValue = options.get(name);
const [ value, setValue ] = useState(initialValue);
async function wrappedSetValue(newValue: string) {
await options.save(name, newValue);
+
+ if (needsRefresh) {
+ reloadFrontendApp(`option change: ${name}`);
+ }
};
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
diff --git a/apps/client/src/widgets/type_widgets/options/appearance.tsx b/apps/client/src/widgets/type_widgets/options/appearance.tsx
index 6743c4a81..9d5fa674d 100644
--- a/apps/client/src/widgets/type_widgets/options/appearance.tsx
+++ b/apps/client/src/widgets/type_widgets/options/appearance.tsx
@@ -1,31 +1,68 @@
+import { useEffect, useState } from "preact/hooks";
import { t } from "../../../services/i18n";
import { isMobile, reloadFrontendApp } from "../../../services/utils";
+import Column from "../../react/Column";
import FormRadioGroup from "../../react/FormRadioGroup";
+import FormSelect from "../../react/FormSelect";
import { useTriliumOption } from "../../react/hooks";
import OptionsSection from "./components/OptionsSection";
+import server from "../../../services/server";
+
+interface Theme {
+ val: string;
+ title: string;
+ noteId?: string;
+}
+
+const BUILTIN_THEMES: Theme[] = [
+ { val: "next", title: t("theme.triliumnext") },
+ { val: "next-light", title: t("theme.triliumnext-light") },
+ { val: "next-dark", title: t("theme.triliumnext-dark") },
+ { val: "auto", title: t("theme.auto_theme") },
+ { val: "light", title: t("theme.light_theme") },
+ { val: "dark", title: t("theme.dark_theme") }
+]
export default function AppearanceSettings() {
- const [ layoutOrientation, setLayoutOrientation ] = useTriliumOption("layoutOrientation");
+ const [ layoutOrientation, setLayoutOrientation ] = useTriliumOption("layoutOrientation", true);
+ const [ theme, setTheme ] = useTriliumOption("theme", true);
+
+ const [ themes, setThemes ] = useState([]);
+
+ useEffect(() => {
+ server.get("options/user-themes").then((userThemes) => {
+ setThemes([
+ ...BUILTIN_THEMES,
+ ...userThemes
+ ])
+ });
+ }, []);
return (
-
- {!isMobile() && {t("theme.layout-vertical-title")} - {t("theme.layout-vertical-description")}>,
- value: "vertical"
- },
- {
- label: <>{t("theme.layout-horizontal-title")} - {t("theme.layout-horizontal-description")}>,
- value: "horizontal"
- }
- ]}
- currentValue={layoutOrientation} onChange={async (newValue) => {
- await setLayoutOrientation(newValue);
- reloadFrontendApp("layout orientation change");
- }}
- />}
-
+ <>
+
+ {!isMobile() && {t("theme.layout-vertical-title")} - {t("theme.layout-vertical-description")}>,
+ value: "vertical"
+ },
+ {
+ label: <>{t("theme.layout-horizontal-title")} - {t("theme.layout-horizontal-description")}>,
+ value: "horizontal"
+ }
+ ]}
+ currentValue={layoutOrientation} onChange={setLayoutOrientation}
+ />}
+
+
+
+
+
+
+
+
+ >
)
}
\ No newline at end of file
diff --git a/apps/client/src/widgets/type_widgets/options/appearance/theme.ts b/apps/client/src/widgets/type_widgets/options/appearance/theme.ts
index 66903babb..a183815ed 100644
--- a/apps/client/src/widgets/type_widgets/options/appearance/theme.ts
+++ b/apps/client/src/widgets/type_widgets/options/appearance/theme.ts
@@ -6,14 +6,7 @@ import type { OptionMap } from "@triliumnext/commons";
const TPL = /*html*/`