diff --git a/apps/client/src/widgets/react/FormRadioGroup.tsx b/apps/client/src/widgets/react/FormRadioGroup.tsx index ccfd0cf84..ae5fac2fd 100644 --- a/apps/client/src/widgets/react/FormRadioGroup.tsx +++ b/apps/client/src/widgets/react/FormRadioGroup.tsx @@ -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} ) diff --git a/apps/client/src/widgets/react/hooks.tsx b/apps/client/src/widgets/react/hooks.tsx index 04ae86ef2..6ea511be9 100644 --- a/apps/client/src/widgets/react/hooks.tsx +++ b/apps/client/src/widgets/react/hooks.tsx @@ -142,6 +142,14 @@ export function useTriliumOptionInt(name: OptionNames): [number, (newValue: numb ] } +export function useTriliumOptionJson(name: OptionNames): [ T, (newValue: T) => Promise ] { + 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. * diff --git a/apps/client/src/widgets/type_widgets/content_widget.tsx b/apps/client/src/widgets/type_widgets/content_widget.tsx index cf50cb3f3..b847dd04d 100644 --- a/apps/client/src/widgets/type_widgets/content_widget.tsx +++ b/apps/client/src/widgets/type_widgets/content_widget.tsx @@ -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"; diff --git a/apps/client/src/widgets/type_widgets/options/components/CheckboxList.tsx b/apps/client/src/widgets/type_widgets/options/components/CheckboxList.tsx new file mode 100644 index 000000000..fedfe1d86 --- /dev/null +++ b/apps/client/src/widgets/type_widgets/options/components/CheckboxList.tsx @@ -0,0 +1,40 @@ +import { useEffect, useState } from "preact/hooks"; + +interface CheckboxListProps { + values: T[]; + keyProperty: keyof T; + titleProperty?: keyof T; + currentValue: string[]; + onChange: (newValues: string[]) => void; +} + +export default function CheckboxList({ values, keyProperty, titleProperty, currentValue, onChange }: CheckboxListProps) { + 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 ( +
    + {values.map(value => ( +
  • + +
  • + ))} +
+ ) +} \ No newline at end of file diff --git a/apps/client/src/widgets/type_widgets/options/i18n.tsx b/apps/client/src/widgets/type_widgets/options/i18n.tsx index 796e3b0f4..e424ef791 100644 --- a/apps/client/src/widgets/type_widgets/options/i18n.tsx +++ b/apps/client/src/widgets/type_widgets/options/i18n.tsx @@ -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 ( <> + ) } @@ -115,4 +117,21 @@ function DateSettings() { ) +} + +function ContentLanguages() { + const locales = useMemo(() => getAvailableLocales(), []); + const [ languages, setLanguages ] = useTriliumOptionJson("languages"); + + return ( + + {t("content_language.description")} + + + + ); } \ No newline at end of file diff --git a/apps/client/src/widgets/type_widgets/options/i18n/language.ts b/apps/client/src/widgets/type_widgets/options/i18n/language.ts deleted file mode 100644 index 7b38067a9..000000000 --- a/apps/client/src/widgets/type_widgets/options/i18n/language.ts +++ /dev/null @@ -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*/` -
-

${t("content_language.title")}

-

${t("content_language.description")}

- -
    -
- - -
-`; - -export default class LanguageOptions extends OptionsWidget { - - private $languagesContainer!: JQuery; - - 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 = $('') - .attr("data-language-id", locale.id) - .prop("checked", enabledLanguages.includes(locale.id)); - const wrapper = $(`