diff --git a/src/components/HeartRating.astro b/src/components/HeartRating.astro index 7e4c39a..cfef817 100644 --- a/src/components/HeartRating.astro +++ b/src/components/HeartRating.astro @@ -28,8 +28,13 @@ const HERZ_PFAD =

{t.shop.filters}

{t.shop.categoryLabel} -
+
-
@@ -60,14 +61,23 @@ const t = useTranslations(lang);
- - + {t.shop.minRatingLabel} +
+ + +
+ + {[4, 3, 2, 1].map((n) => ( + + ))} +
+

{t.shop.filterNote}

@@ -160,49 +170,70 @@ const t = useTranslations(lang); sorted.forEach((el) => grid.appendChild(el)); } - [preisRange, verfuegbarChk, bewertungSel, sortSel].forEach((el) => el.addEventListener("input", apply)); + [preisRange, verfuegbarChk, sortSel].forEach((el) => el.addEventListener("input", apply)); - const catFilter = document.getElementById("cat-filter"); - const catFilterBtn = document.getElementById("cat-filter-btn"); - const catFilterCurrent = document.getElementById("cat-filter-current"); - const catFilterOptions = Array.from(catFilter.querySelectorAll(".cat-filter-option")); + // Eigene Dropdowns (statt nativer -
@@ -60,14 +61,23 @@ const t = useTranslations(lang);
- - + {t.shop.minRatingLabel} +
+ + +
+ + {[4, 3, 2, 1].map((n) => ( + + ))} +
+

{t.shop.filterNote}

@@ -160,49 +170,70 @@ const t = useTranslations(lang); sorted.forEach((el) => grid.appendChild(el)); } - [preisRange, verfuegbarChk, bewertungSel, sortSel].forEach((el) => el.addEventListener("input", apply)); + [preisRange, verfuegbarChk, sortSel].forEach((el) => el.addEventListener("input", apply)); - const catFilter = document.getElementById("cat-filter"); - const catFilterBtn = document.getElementById("cat-filter-btn"); - const catFilterCurrent = document.getElementById("cat-filter-current"); - const catFilterOptions = Array.from(catFilter.querySelectorAll(".cat-filter-option")); + // Eigene Dropdowns (statt nativer -
@@ -60,14 +61,23 @@ const t = useTranslations(lang);
- - + {t.shop.minRatingLabel} +
+ + +
+ + {[4, 3, 2, 1].map((n) => ( + + ))} +
+

{t.shop.filterNote}

@@ -160,49 +170,70 @@ const t = useTranslations(lang); sorted.forEach((el) => grid.appendChild(el)); } - [preisRange, verfuegbarChk, bewertungSel, sortSel].forEach((el) => el.addEventListener("input", apply)); + [preisRange, verfuegbarChk, sortSel].forEach((el) => el.addEventListener("input", apply)); - const catFilter = document.getElementById("cat-filter"); - const catFilterBtn = document.getElementById("cat-filter-btn"); - const catFilterCurrent = document.getElementById("cat-filter-current"); - const catFilterOptions = Array.from(catFilter.querySelectorAll(".cat-filter-option")); + // Eigene Dropdowns (statt nativer -
@@ -65,14 +66,27 @@ const t = useTranslations(lang);
- - + {t.shop.minRatingLabel} + {/* Eigenes Dropdown statt eines nativen + +
+ + {[4, 3, 2, 1].map((n) => ( + + ))} +
+

{t.shop.filterNote}

@@ -165,52 +179,70 @@ const t = useTranslations(lang); sorted.forEach((el) => grid.appendChild(el)); } - [preisRange, verfuegbarChk, bewertungSel, sortSel].forEach((el) => el.addEventListener("input", apply)); + [preisRange, verfuegbarChk, sortSel].forEach((el) => el.addEventListener("input", apply)); - // Eigenes Kategorie-Dropdown (statt nativem -Felder): Der Browser kann in seiner eigenen + // Auswahlliste weder unsere Kategorie-Struktur noch Verlaufs-Farben darstellen — deshalb hier + // eine eigene, gestaltbare Liste. Ein gemeinsamer Code für BEIDE Dropdowns (Kategorie und + // Mindestbewertung), damit sich beide identisch verhalten und es nur eine Stelle zum Pflegen + // gibt. Verhalten wie beim Sprach-Umschalter im Header. + const dropdowns = Array.from(document.querySelectorAll("[data-dropdown]")).map((root) => { + const btn = root.querySelector("[data-dropdown-btn]"); + const current = root.querySelector("[data-dropdown-current]"); + const hidden = root.querySelector("input[type=hidden]"); + const options = Array.from(root.querySelectorAll(".cat-filter-option")); - function selectKategorie(value, label) { - kategorieSel.value = value; - catFilterCurrent.textContent = label; - catFilterOptions.forEach((opt) => opt.setAttribute("aria-selected", String(opt.dataset.value === value))); - apply(); - } - - catFilterBtn.addEventListener("click", (e) => { - e.stopPropagation(); - const open = catFilter.classList.toggle("open"); - catFilterBtn.setAttribute("aria-expanded", open ? "true" : "false"); - }); - catFilterOptions.forEach((opt) => { - opt.addEventListener("click", () => { - selectKategorie(opt.dataset.value, opt.dataset.label); - catFilter.classList.remove("open"); - catFilterBtn.setAttribute("aria-expanded", "false"); - }); - }); - document.addEventListener("click", () => { - catFilter.classList.remove("open"); - catFilterBtn.setAttribute("aria-expanded", "false"); - }); - document.addEventListener("keydown", (e) => { - if (e.key === "Escape") { - catFilter.classList.remove("open"); - catFilterBtn.setAttribute("aria-expanded", "false"); + function select(value) { + const opt = options.find((o) => o.dataset.value === value); + if (!opt) return; + hidden.value = value; + // Optionen ohne eigenes data-label (z.B. die Herzen-Zeilen) übernehmen ihren kompletten + // Inhalt in den Knopf — so stehen dort dieselben Verlaufs-Herzen wie in der Liste. + if (opt.dataset.label !== undefined) current.textContent = opt.dataset.label; + else current.innerHTML = opt.innerHTML; + options.forEach((o) => o.setAttribute("aria-selected", String(o === opt))); + apply(); } + + function close() { + root.classList.remove("open"); + btn.setAttribute("aria-expanded", "false"); + } + + btn.addEventListener("click", (e) => { + e.stopPropagation(); + const willOpen = !root.classList.contains("open"); + // Immer erst alle anderen schließen, damit nie zwei Listen gleichzeitig offen sind. + document.querySelectorAll("[data-dropdown].open").forEach((other) => { + other.classList.remove("open"); + other.querySelector("[data-dropdown-btn]").setAttribute("aria-expanded", "false"); + }); + if (willOpen) { + root.classList.add("open"); + btn.setAttribute("aria-expanded", "true"); + } + }); + + options.forEach((opt) => { + opt.addEventListener("click", () => { + select(opt.dataset.value); + close(); + }); + }); + + return { root, close, select, options }; }); + document.addEventListener("click", () => dropdowns.forEach((d) => d.close())); + document.addEventListener("keydown", (e) => { + if (e.key === "Escape") dropdowns.forEach((d) => d.close()); + }); + + // Voreinstellung über die Adresszeile (z.B. /shop/?kategorie=diy-plushies), damit Links aus + // der Navigation weiterhin direkt mit gesetztem Filter landen. const params = new URLSearchParams(location.search); const preset = params.get("kategorie"); - if (preset) { - const match = catFilterOptions.find((opt) => opt.dataset.value === preset); - if (match) selectKategorie(preset, match.dataset.label); - } + if (preset) dropdowns.forEach((d) => d.select(preset)); apply(); diff --git a/src/styles/global.css b/src/styles/global.css index 66b8ca9..6589b2f 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -916,6 +916,11 @@ a:focus-visible, button:focus-visible { .cat-filter-option[aria-selected="true"] { background: color-mix(in srgb, var(--c-accent) 20%, transparent); font-weight: 700; } .cat-filter-option.cat-filter-sub { padding-left: 1.4em; font-size: 0.88rem; opacity: 0.92; } .cat-filter-option.cat-filter-sub::before { content: "— "; opacity: 0.6; } +/* Zeilen des Mindestbewertung-Filters: Herzen und das "+" nebeneinander auf einer Linie. */ +.cat-filter-option.rating-option { display: flex; align-items: center; gap: 0.45em; } +.rating-plus { color: var(--c-text-muted); font-weight: 700; } +/* Auch im geschlossenen Knopf sollen die Herzen sauber neben dem Pfeil sitzen. */ +.cat-filter-btn .heart-rating { vertical-align: middle; } .shop-toolbar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.2rem; } .legal-hint { margin-top: 2rem; } .breadcrumb { margin-bottom: 0.6rem; font-size: 0.95rem; }