select styled
This commit is contained in:
+25
-21
@@ -1,6 +1,10 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Mateusz Gruszczynski @linuxiarz.pl
|
||||
* Source-Available Code / Dual-Licensed.
|
||||
*
|
||||
* Free for non-commercial and evaluation use under terms of BSL/GPLv3.
|
||||
* Commercial or production use requires a valid paid license.
|
||||
* See LICENSE file in repository root for details.
|
||||
*/
|
||||
|
||||
import { api } from "@rustpad/api";
|
||||
@@ -28,7 +32,7 @@ function icon(name) {
|
||||
}
|
||||
|
||||
function closeOtherPickers(except = null) {
|
||||
document.querySelectorAll(".header-preference-picker[open]").forEach(details => {
|
||||
document.querySelectorAll(".app-select--header[open]").forEach(details => {
|
||||
if (details !== except) details.open = false;
|
||||
});
|
||||
}
|
||||
@@ -36,19 +40,19 @@ function closeOtherPickers(except = null) {
|
||||
function preferenceButton({ value, label, selected, languageCode }) {
|
||||
const button = document.createElement("button");
|
||||
button.type = "button";
|
||||
button.className = "header-preference-option";
|
||||
button.className = "app-select-option";
|
||||
button.dataset.value = value;
|
||||
button.setAttribute("role", "option");
|
||||
button.setAttribute("aria-selected", selected ? "true" : "false");
|
||||
if (languageCode) button.lang = languageCode;
|
||||
|
||||
const check = document.createElement("span");
|
||||
check.className = "header-preference-option__check";
|
||||
check.className = "app-select-option__check";
|
||||
check.setAttribute("aria-hidden", "true");
|
||||
check.textContent = selected ? "✓" : "";
|
||||
|
||||
const copy = document.createElement("span");
|
||||
copy.className = "header-preference-option__copy";
|
||||
copy.className = "app-select-option__copy";
|
||||
copy.textContent = label;
|
||||
|
||||
button.append(check, copy);
|
||||
@@ -57,29 +61,29 @@ function preferenceButton({ value, label, selected, languageCode }) {
|
||||
|
||||
function createPicker(kind) {
|
||||
const details = document.createElement("details");
|
||||
details.className = `header-preference-picker header-preference-picker--${kind}`;
|
||||
details.className = `app-select app-select--header app-select--${kind}`;
|
||||
details.dataset.preferenceKind = kind;
|
||||
|
||||
const summary = document.createElement("summary");
|
||||
summary.className = "header-preference-trigger";
|
||||
summary.className = "app-select-trigger";
|
||||
|
||||
const iconWrap = document.createElement("span");
|
||||
iconWrap.className = "header-preference-trigger__icon";
|
||||
iconWrap.className = "app-select-trigger__icon";
|
||||
iconWrap.innerHTML = icon(kind === "theme" ? getTheme() : "language");
|
||||
|
||||
const compactValue = document.createElement("span");
|
||||
compactValue.className = "header-preference-trigger__value";
|
||||
compactValue.className = "app-select-trigger__value";
|
||||
compactValue.setAttribute("aria-hidden", "true");
|
||||
|
||||
const chevron = document.createElement("span");
|
||||
chevron.className = "header-preference-trigger__chevron";
|
||||
chevron.className = "app-select-trigger__chevron";
|
||||
chevron.setAttribute("aria-hidden", "true");
|
||||
chevron.textContent = "▾";
|
||||
|
||||
summary.append(iconWrap, compactValue, chevron);
|
||||
|
||||
const menu = document.createElement("div");
|
||||
menu.className = "header-preference-menu";
|
||||
menu.className = "app-select-menu";
|
||||
menu.setAttribute("role", "listbox");
|
||||
|
||||
details.append(summary, menu);
|
||||
@@ -102,9 +106,9 @@ function renderLanguagePicker(details) {
|
||||
const languages = getAvailableLanguages();
|
||||
const current = getLanguage();
|
||||
const selected = languages.find(language => language.code === current) || languages[0];
|
||||
const summary = details.querySelector(".header-preference-trigger");
|
||||
const value = details.querySelector(".header-preference-trigger__value");
|
||||
const menu = details.querySelector(".header-preference-menu");
|
||||
const summary = details.querySelector(".app-select-trigger");
|
||||
const value = details.querySelector(".app-select-trigger__value");
|
||||
const menu = details.querySelector(".app-select-menu");
|
||||
const label = t("common.language", {}, "Language");
|
||||
|
||||
summary.setAttribute("aria-label", label);
|
||||
@@ -121,10 +125,10 @@ function renderLanguagePicker(details) {
|
||||
|
||||
function renderThemePicker(details) {
|
||||
const current = getTheme();
|
||||
const summary = details.querySelector(".header-preference-trigger");
|
||||
const iconWrap = details.querySelector(".header-preference-trigger__icon");
|
||||
const value = details.querySelector(".header-preference-trigger__value");
|
||||
const menu = details.querySelector(".header-preference-menu");
|
||||
const summary = details.querySelector(".app-select-trigger");
|
||||
const iconWrap = details.querySelector(".app-select-trigger__icon");
|
||||
const value = details.querySelector(".app-select-trigger__value");
|
||||
const menu = details.querySelector(".app-select-menu");
|
||||
const label = t("profile.theme", {}, "Interface theme");
|
||||
|
||||
summary.setAttribute("aria-label", label);
|
||||
@@ -145,12 +149,12 @@ function renderPicker(details) {
|
||||
}
|
||||
|
||||
function renderAll() {
|
||||
document.querySelectorAll(".header-preference-picker").forEach(renderPicker);
|
||||
document.querySelectorAll(".app-select--header").forEach(renderPicker);
|
||||
}
|
||||
|
||||
function bindPicker(details) {
|
||||
details.querySelector(".header-preference-menu")?.addEventListener("click", async event => {
|
||||
const button = event.target.closest(".header-preference-option");
|
||||
details.querySelector(".app-select-menu")?.addEventListener("click", async event => {
|
||||
const button = event.target.closest(".app-select-option");
|
||||
if (!(button instanceof HTMLButtonElement)) return;
|
||||
const next = button.dataset.value || "";
|
||||
const kind = details.dataset.preferenceKind;
|
||||
@@ -209,7 +213,7 @@ export function initHeaderPreferences() {
|
||||
|
||||
document.addEventListener("pointerdown", event => {
|
||||
const target = event.target instanceof Element ? event.target : null;
|
||||
if (!target?.closest(".header-preference-picker")) closeOtherPickers();
|
||||
if (!target?.closest(".app-select--header")) closeOtherPickers();
|
||||
}, { passive: true });
|
||||
document.addEventListener("keydown", event => {
|
||||
if (event.key === "Escape") closeOtherPickers();
|
||||
|
||||
Reference in New Issue
Block a user