From 4dc773c1a3d9ae093fb84067bcf80a59ad7da5d9 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 12 Dec 2025 18:29:40 +0200 Subject: [PATCH] refactor(status_bar/language): stop reusing UI for greater customisibility --- apps/client/src/widgets/layout/StatusBar.tsx | 47 ++++++++++++-- .../src/widgets/ribbon/BasicPropertiesTab.tsx | 55 ++++++++-------- .../options/components/LocaleSelector.tsx | 64 +++++++++---------- 3 files changed, 99 insertions(+), 67 deletions(-) diff --git a/apps/client/src/widgets/layout/StatusBar.tsx b/apps/client/src/widgets/layout/StatusBar.tsx index 3cea9e988..deb01da1c 100644 --- a/apps/client/src/widgets/layout/StatusBar.tsx +++ b/apps/client/src/widgets/layout/StatusBar.tsx @@ -3,10 +3,15 @@ import "./StatusBar.css"; import FNote from "../../entities/fnote"; import { t } from "../../services/i18n"; import { openInAppHelpFromUrl } from "../../services/utils"; -import { FormListItem } from "../react/FormList"; +import { FormDropdownDivider, FormListItem } from "../react/FormList"; import { useNoteContext } from "../react/hooks"; -import { NoteLanguageSelector } from "../ribbon/BasicPropertiesTab"; +import { ContentLanguagesModal, NoteLanguageSelector, useLanguageSwitcher } from "../ribbon/BasicPropertiesTab"; import Breadcrumb from "./Breadcrumb"; +import { useState } from "preact/hooks"; +import { createPortal } from "preact/compat"; +import { useProcessedLocales } from "../type_widgets/options/components/LocaleSelector"; +import Dropdown from "../react/Dropdown"; +import { Locale } from "@triliumnext/commons"; interface StatusBarContext { note: FNote; @@ -32,16 +37,44 @@ export default function StatusBar() { } function LanguageSwitcher({ note }: StatusBarContext) { + const [ modalShown, setModalShown ] = useState(false); + const { locales, DEFAULT_LOCALE, currentNoteLanguage, setCurrentNoteLanguage } = useLanguageSwitcher(note); + const { activeLocale, processedLocales } = useProcessedLocales(locales, DEFAULT_LOCALE, currentNoteLanguage ?? DEFAULT_LOCALE.id); + return ( - + + {processedLocales.map(locale => { + if (typeof locale === "object") { + return setCurrentNoteLanguage(locale.id)} + >{locale.name} + } else { + return + } + })} + openInAppHelpFromUrl("veGu4faJErEM")} icon="bx bx-help-circle" >{t("note_language.help-on-languages")} + setModalShown(true)} + icon="bx bx-cog" + >{t("note_language.configure-languages")} + + {createPortal( + , + document.body )} - compact - /> + ); } + +export function getLocaleName(locale: Locale | null | undefined) { + if (!locale) return ""; + if (!locale.id) return "-"; + return locale.id.toLocaleUpperCase(); +} diff --git a/apps/client/src/widgets/ribbon/BasicPropertiesTab.tsx b/apps/client/src/widgets/ribbon/BasicPropertiesTab.tsx index dda9a7830..761577949 100644 --- a/apps/client/src/widgets/ribbon/BasicPropertiesTab.tsx +++ b/apps/client/src/widgets/ribbon/BasicPropertiesTab.tsx @@ -330,12 +330,32 @@ function NoteLanguageSwitch({ note }: { note?: FNote | null }) { ); } -export function NoteLanguageSelector({ note, extraChildren, ...restProps }: { - note: FNote | null | undefined, - extraChildren?: ComponentChildren, - compact?: boolean; -}) { +export function NoteLanguageSelector({ note }: { note: FNote | null | undefined }) { const [ modalShown, setModalShown ] = useState(false); + const { locales, DEFAULT_LOCALE, currentNoteLanguage, setCurrentNoteLanguage } = useLanguageSwitcher(note); + + return ( + <> + + setModalShown(true)} + icon="bx bx-cog" + >{t("note_language.configure-languages")} + } + /> + {createPortal( + , + document.body + )} + + ); +} + +export function useLanguageSwitcher(note: FNote | null | undefined) { const [ languages ] = useTriliumOption("languages"); const DEFAULT_LOCALE = { id: "", @@ -347,31 +367,10 @@ export function NoteLanguageSelector({ note, extraChildren, ...restProps }: { const filteredLanguages = getAvailableLocales().filter((l) => typeof l !== "object" || enabledLanguages.includes(l.id)); return filteredLanguages; }, [ languages ]); - - return ( - <> - - {extraChildren} - setModalShown(true)} - icon="bx bx-cog" - >{t("note_language.configure-languages")} - } - {...restProps} - /> - {createPortal( - , - document.body - )} - - ); + return { locales, DEFAULT_LOCALE, currentNoteLanguage, setCurrentNoteLanguage }; } -function ContentLanguagesModal({ modalShown, setModalShown }: { modalShown: boolean, setModalShown: (shown: boolean) => void }) { +export function ContentLanguagesModal({ modalShown, setModalShown }: { modalShown: boolean, setModalShown: (shown: boolean) => void }) { return ( void, defaultLocale?: Locale, extraChildren?: ComponentChildren, - compact?: boolean; }) { + const currentValueWithDefault = currentValue ?? defaultLocale?.id ?? ""; + const { activeLocale, processedLocales } = useProcessedLocales(locales, defaultLocale, currentValueWithDefault); + return ( + + {processedLocales.map(locale => { + if (typeof locale === "object") { + return { + onChange(locale.id); + }} + >{locale.name} + } else { + return + } + })} + {extraChildren && ( + <> + + {extraChildren} + + )} + + ); +} + +export function useProcessedLocales(locales: Locale[], defaultLocale: Locale | undefined, currentValue: string) { const activeLocale = defaultLocale?.id === currentValue ? defaultLocale : locales.find(l => l.id === currentValue); const processedLocales = useMemo(() => { @@ -36,35 +63,8 @@ export function LocaleSelector({ id, locales, currentValue, onChange, defaultLoc ]; } - if (extraChildren) { - items.push("---"); - } return items; - }, [ locales ]); + }, [ locales, defaultLocale ]); - return ( - - {processedLocales.map(locale => { - if (typeof locale === "object") { - return { - onChange(locale.id); - }} - >{locale.name} - } else { - return - } - })} - {extraChildren} - - ); -} - -function getLocaleName(locale: Locale | null | undefined, compact: boolean | undefined) { - if (!locale) return ""; - if (!compact) return locale.name; - if (!locale.id) return "-"; - return locale.id.toLocaleUpperCase(); + return { activeLocale, processedLocales }; }