From a479a3c08df06ce2645a6c9db00238a963980a0d Mon Sep 17 00:00:00 2001 From: rajchales-monito Date: Thu, 30 Jul 2026 09:52:16 +0200 Subject: [PATCH] Use custom flag language picker --- public/admin.css | 90 ++++++++++++++++++++++++++++++++++++++++------- public/admin.html | 5 +-- public/admin.js | 80 ++++++++++++++++++++++++++++++++--------- 3 files changed, 143 insertions(+), 32 deletions(-) diff --git a/public/admin.css b/public/admin.css index 307026f..e318781 100644 --- a/public/admin.css +++ b/public/admin.css @@ -247,22 +247,86 @@ label { display: grid; gap: 6px; color: var(--muted); font-size: 13px; } position: relative; display: block; } -.language-select select { - padding-left: 42px; -} -.selected-language-flag { +.native-language-select { position: absolute; - z-index: 1; - left: 12px; - top: 50%; - width: 18px; - height: 13px; - transform: translateY(-50%); - line-height: 1; + width: 1px; + height: 1px; + opacity: 0; pointer-events: none; } -.selected-language-flag .flag-image { - display: block; +.language-picker { + position: relative; + width: 100%; + height: 38px; + padding: 0 30px 0 11px; + display: flex; + align-items: center; + gap: 8px; + border: 1px solid var(--line); + border-radius: 7px; + background: white; + color: var(--ink); + font-size: 13px; + text-align: left; +} +.language-picker::after { + content: ""; + position: absolute; + right: 11px; + top: 50%; + width: 7px; + height: 7px; + border-right: 1px solid var(--muted); + border-bottom: 1px solid var(--muted); + transform: translateY(-50%); + rotate: 45deg; +} +.language-picker span:last-child { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.language-menu { + position: absolute; + z-index: 20; + top: calc(100% + 5px); + left: 0; + right: 0; + max-height: 230px; + overflow: auto; + padding: 5px; + border: 1px solid var(--line); + border-radius: 8px; + background: white; + box-shadow: 0 14px 30px rgb(23 33 29 / 14%); +} +.language-option { + width: 100%; + min-height: 32px; + padding: 6px 8px; + display: flex; + align-items: center; + gap: 8px; + border: 0; + border-radius: 6px; + background: transparent; + color: var(--ink); + font-size: 13px; + text-align: left; +} +.language-option:hover, +.language-option.active { + background: #eaf6f1; + color: var(--brand); +} +.language-option span:last-child { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.language-picker .flag-image, +.language-option .flag-image { + flex: 0 0 auto; } .effect-settings .full-row { display: grid; diff --git a/public/admin.html b/public/admin.html index b0d8eb8..9981f31 100644 --- a/public/admin.html +++ b/public/admin.html @@ -124,8 +124,9 @@ Auto pouzije jazyk ze stranky. Default je vychozi jazyk i zaloha. diff --git a/public/admin.js b/public/admin.js index 5f2e367..63ddc5d 100644 --- a/public/admin.js +++ b/public/admin.js @@ -114,9 +114,26 @@ $("#settingsForm").brandHex.addEventListener("input", (event) => { if (color) $("#settingsForm").brand.value = color; }); $("#settingsForm").defaultLanguage.addEventListener("change", () => { - updateDefaultLanguageFlag(); + updateDefaultLanguagePicker(); renderWidgetCopyFields($("#settingsForm").defaultLanguage.value); }); +$("#defaultLanguagePicker").addEventListener("click", (event) => { + event.stopPropagation(); + toggleDefaultLanguageMenu(); +}); +$("#defaultLanguageMenu").addEventListener("click", (event) => { + const option = event.target.closest("[data-language-option]"); + if (!option) return; + $("#settingsForm").defaultLanguage.value = option.dataset.languageOption; + $("#settingsForm").defaultLanguage.dispatchEvent(new Event("change", { bubbles: true })); + closeDefaultLanguageMenu(); +}); +document.addEventListener("click", (event) => { + if (!event.target.closest(".language-select")) closeDefaultLanguageMenu(); +}); +document.addEventListener("keydown", (event) => { + if (event.key === "Escape") closeDefaultLanguageMenu(); +}); window.addEventListener("focus", syncAttentionWithUnread); document.addEventListener("visibilitychange", () => { @@ -464,14 +481,12 @@ function renderWidgetLanguageOptions(form, settings) { .map(([code, label]) => ``) .join(""); form.defaultLanguage.innerHTML = options; + $("#defaultLanguageMenu").innerHTML = WIDGET_LANGUAGES + .map(([code, label]) => languageOptionHtml(code, label)) + .join(""); form.widgetLanguageMode.value = language.mode === "fixed" ? "default" : language.mode || "auto"; form.defaultLanguage.value = language.defaultLanguage || "cs"; - updateDefaultLanguageFlag(); -} - -function languageFlag(language) { - const countryCode = languageCountryCode(language); - return countryCode ? flagEmoji(countryCode) : ""; + updateDefaultLanguagePicker(); } function languageCountryCode(language) { @@ -484,20 +499,51 @@ function languageCountryCode(language) { return countries[language] || ""; } -function flagEmoji(countryCode) { +function flagImageHtml(countryCode) { const code = String(countryCode || "").toUpperCase(); - if (!/^[A-Z]{2}$/.test(code)) return ""; - return [...code].map((letter) => String.fromCodePoint(127397 + letter.charCodeAt(0))).join(""); + if (!/^[A-Z]{2}$/.test(code)) return `?`; + return ``; } -function updateDefaultLanguageFlag() { - const flag = $("#defaultLanguageFlag"); +function languageOptionHtml(language, label) { + const countryCode = languageCountryCode(language); + return ` + + `; +} + +function updateDefaultLanguagePicker() { + const picker = $("#defaultLanguagePicker"); + const menu = $("#defaultLanguageMenu"); const select = $("#settingsForm")?.defaultLanguage; - if (!flag || !select) return; - const countryCode = languageCountryCode(select.value || "cs"); - flag.innerHTML = countryCode - ? `` - : ""; + if (!picker || !select) return; + const language = select.value || "cs"; + const item = WIDGET_LANGUAGES.find(([code]) => code === language) || WIDGET_LANGUAGES[0]; + picker.innerHTML = `${flagImageHtml(languageCountryCode(language))}${item[0].toUpperCase()} - ${escapeHtml(item[1])}`; + if (menu) { + menu.querySelectorAll("[data-language-option]").forEach((option) => { + option.classList.toggle("active", option.dataset.languageOption === language); + }); + } +} + +function toggleDefaultLanguageMenu() { + const menu = $("#defaultLanguageMenu"); + const picker = $("#defaultLanguagePicker"); + const isOpen = menu && !menu.classList.contains("hidden"); + if (isOpen) closeDefaultLanguageMenu(); + else { + menu?.classList.remove("hidden"); + picker?.setAttribute("aria-expanded", "true"); + } +} + +function closeDefaultLanguageMenu() { + $("#defaultLanguageMenu")?.classList.add("hidden"); + $("#defaultLanguagePicker")?.setAttribute("aria-expanded", "false"); } function renderWidgetCopyFields(language) {