feat(react/settings): port content languages

This commit is contained in:
Elian Doran 2025-08-15 10:26:25 +03:00
parent c039f06c2b
commit c368ec3c38
No known key found for this signature in database
6 changed files with 70 additions and 67 deletions

View File

@ -40,7 +40,8 @@ function FormRadio({ name, value, label, currentValue, onChange, labelClassName
name={useUniqueName(name)}
value={value}
checked={value === currentValue}
onChange={e => onChange((e.target as HTMLInputElement).value)} />
onChange={e => onChange((e.target as HTMLInputElement).value)}
/>
{label}
</label>
)

View File

@ -142,6 +142,14 @@ export function useTriliumOptionInt(name: OptionNames): [number, (newValue: numb
]
}
export function useTriliumOptionJson<T>(name: OptionNames): [ T, (newValue: T) => Promise<void> ] {
const [ value, setValue ] = useTriliumOption(name);
return [
(JSON.parse(value) as T),
(newValue => setValue(JSON.stringify(newValue)))
];
}
/**
* Generates a unique name via a random alphanumeric string of a fixed length.
*

View File

@ -24,7 +24,6 @@ import HtmlImportTagsOptions from "./options/other/html_import_tags.js";
import BackendLogWidget from "./content/backend_log.js";
import AttachmentErasureTimeoutOptions from "./options/other/attachment_erasure_timeout.js";
import MultiFactorAuthenticationOptions from './options/multi_factor_authentication.js';
import LocalizationOptions from "./options/i18n/i18n.js";
import CodeBlockOptions from "./options/text_notes/code_block.js";
import EditorOptions from "./options/text_notes/editor.js";
import ShareSettingsOptions from "./options/other/share_settings.js";
@ -32,7 +31,6 @@ import AiSettingsOptions from "./options/ai_settings.js";
import type FNote from "../../entities/fnote.js";
import type NoteContextAwareWidget from "../note_context_aware_widget.js";
import { t } from "../../services/i18n.js";
import LanguageOptions from "./options/i18n/language.js";
import type BasicWidget from "../basic_widget.js";
import CodeTheme from "./options/code_notes/code_theme.js";
import EditorFeaturesOptions from "./options/text_notes/features.js";

View File

@ -0,0 +1,40 @@
import { useEffect, useState } from "preact/hooks";
interface CheckboxListProps<T> {
values: T[];
keyProperty: keyof T;
titleProperty?: keyof T;
currentValue: string[];
onChange: (newValues: string[]) => void;
}
export default function CheckboxList<T>({ values, keyProperty, titleProperty, currentValue, onChange }: CheckboxListProps<T>) {
function toggleValue(value: string) {
if (currentValue.includes(value)) {
// Already there, needs removing.
onChange(currentValue.filter(v => v !== value));
} else {
// Not there, needs adding.
onChange([ ...currentValue, value ]);
}
}
return (
<ul style={{ listStyleType: "none", marginBottom: 0, columnWidth: "400px" }}>
{values.map(value => (
<li>
<label className="tn-checkbox">
<input
type="checkbox"
className="form-check-input"
value={String(value[keyProperty])}
checked={currentValue.includes(String(value[keyProperty]))}
onChange={e => toggleValue((e.target as HTMLInputElement).value)}
/>
{String(value[titleProperty ?? keyProperty] ?? value[keyProperty])}
</label>
</li>
))}
</ul>
)
}

View File

@ -3,7 +3,7 @@ import { getAvailableLocales, t } from "../../../services/i18n";
import FormSelect from "../../react/FormSelect";
import OptionsRow from "./components/OptionsRow";
import OptionsSection from "./components/OptionsSection";
import { useTriliumOption, useTriliumOptionInt } from "../../react/hooks";
import { useTriliumOption, useTriliumOptionInt, useTriliumOptionJson } from "../../react/hooks";
import type { Locale } from "@triliumnext/commons";
import { isElectron, restartDesktopApp } from "../../../services/utils";
import FormRadioGroup, { FormInlineRadioGroup } from "../../react/FormRadioGroup";
@ -11,11 +11,13 @@ import FormText from "../../react/FormText";
import RawHtml from "../../react/RawHtml";
import Admonition from "../../react/Admonition";
import Button from "../../react/Button";
import CheckboxList from "./components/CheckboxList";
export default function InternationalizationOptions() {
return (
<>
<LocalizationOptions />
<ContentLanguages />
</>
)
}
@ -116,3 +118,20 @@ function DateSettings() {
</>
)
}
function ContentLanguages() {
const locales = useMemo(() => getAvailableLocales(), []);
const [ languages, setLanguages ] = useTriliumOptionJson<string[]>("languages");
return (
<OptionsSection title={t("content_language.title")}>
<FormText>{t("content_language.description")}</FormText>
<CheckboxList
values={locales}
keyProperty="id" titleProperty="name"
currentValue={languages} onChange={setLanguages}
/>
</OptionsSection>
);
}

View File

@ -1,63 +0,0 @@
import OptionsWidget from "../options_widget.js";
import type { OptionMap } from "@triliumnext/commons";
import { getAvailableLocales } from "../../../../services/i18n.js";
import { t } from "../../../../services/i18n.js";
const TPL = /*html*/`
<div class="options-section">
<h4>${t("content_language.title")}</h4>
<p class="form-text">${t("content_language.description")}</p>
<ul class="options-languages">
</ul>
<style>
ul.options-languages {
list-style-type: none;
margin-bottom: 0;
column-width: 400px;
}
</style>
</div>
`;
export default class LanguageOptions extends OptionsWidget {
private $languagesContainer!: JQuery<HTMLElement>;
doRender() {
this.$widget = $(TPL);
this.$languagesContainer = this.$widget.find(".options-languages");
}
async save() {
const enabledLanguages: string[] = [];
this.$languagesContainer.find("input:checked").each((i, el) => {
const languageId = $(el).attr("data-language-id");
if (languageId) {
enabledLanguages.push(languageId);
}
});
await this.updateOption("languages", JSON.stringify(enabledLanguages));
}
async optionsLoaded(options: OptionMap) {
const availableLocales = getAvailableLocales();
const enabledLanguages = (JSON.parse(options.languages) as string[]);
this.$languagesContainer.empty();
for (const locale of availableLocales) {
const checkbox = $('<input type="checkbox" class="form-check-input">')
.attr("data-language-id", locale.id)
.prop("checked", enabledLanguages.includes(locale.id));
const wrapper = $(`<label class="tn-checkbox">`)
.append(checkbox)
.on("change", () => this.save())
.append(locale.name);
this.$languagesContainer.append($("<li>").append(wrapper));
}
}
}