Kolejne przeróbki,
This commit is contained in:
@@ -1,31 +1,75 @@
|
||||
import { useState } from "preact/hooks";
|
||||
import { useEffect, useRef, useState } from "preact/hooks";
|
||||
|
||||
/**
|
||||
* @typedef {{ name: string; href: string }} MenuLink
|
||||
* @param {{ links: MenuLink[] }} props
|
||||
*/
|
||||
|
||||
export default function MobileMenu({ links = [] }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const menuRef = useRef(null);
|
||||
const btnRef = useRef(null);
|
||||
|
||||
const toggle = () => setOpen(o => !o);
|
||||
const close = () => setOpen(false);
|
||||
const toggle = (e) => {
|
||||
e?.stopPropagation?.();
|
||||
setOpen((o) => !o);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const onKey = (e) => e.key === "Escape" && close();
|
||||
document.addEventListener("keydown", onKey);
|
||||
return () => document.removeEventListener("keydown", onKey);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
|
||||
const onDocClick = (e) => {
|
||||
const menuEl = menuRef.current;
|
||||
const btnEl = btnRef.current;
|
||||
|
||||
if (menuEl && menuEl.contains(e.target)) return;
|
||||
if (btnEl && btnEl.contains(e.target)) return;
|
||||
|
||||
close();
|
||||
};
|
||||
|
||||
document.addEventListener("mousedown", onDocClick);
|
||||
document.addEventListener("touchstart", onDocClick, { passive: true });
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("mousedown", onDocClick);
|
||||
document.removeEventListener("touchstart", onDocClick);
|
||||
};
|
||||
}, [open]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
ref={btnRef}
|
||||
onClick={toggle}
|
||||
class="f-mobile-toggle md:hidden"
|
||||
aria-label="Menu mobilne"
|
||||
aria-expanded={open}
|
||||
aria-controls="mobile-menu"
|
||||
>
|
||||
{open ? "✖" : "☰"}
|
||||
</button>
|
||||
|
||||
<div class={`f-mobile-menu ${open ? "open" : ""}`}>
|
||||
{links.map(link => (
|
||||
<a
|
||||
href={link.href}
|
||||
class="f-mobile-link"
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
<div
|
||||
class={`f-mobile-backdrop ${open ? "open" : ""}`}
|
||||
onClick={close}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
||||
<div
|
||||
id="mobile-menu"
|
||||
ref={menuRef}
|
||||
class={`f-mobile-menu ${open ? "open" : ""}`}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{links.map((link) => (
|
||||
<a href={link.href} class="f-mobile-link" onClick={close}>
|
||||
{link.name}
|
||||
</a>
|
||||
))}
|
||||
|
||||
@@ -23,7 +23,6 @@ export default function OffersSwitches(props) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
// 🔹 AUTO mode – sam pobieram /api/switches + wysyłam event
|
||||
useEffect(() => {
|
||||
if (isControlled) return;
|
||||
|
||||
@@ -52,7 +51,6 @@ export default function OffersSwitches(props) {
|
||||
setAutoSwitches(sws);
|
||||
setAutoSelected(initial);
|
||||
|
||||
// 🔥 zapisz globalny stan
|
||||
window.fuzSwitchState = {
|
||||
selected: initial,
|
||||
labels,
|
||||
@@ -69,7 +67,7 @@ export default function OffersSwitches(props) {
|
||||
}),
|
||||
);
|
||||
} catch (err) {
|
||||
console.error("Błąd pobierania switchy:", err);
|
||||
console.error("Błąd pobierania przełączników:", err);
|
||||
if (!cancelled) setError("Nie udało się załadować przełączników.");
|
||||
} finally {
|
||||
if (!cancelled) setLoading(false);
|
||||
@@ -93,7 +91,6 @@ export default function OffersSwitches(props) {
|
||||
const next = { ...prev, [id]: value };
|
||||
const labels = buildLabels(autoSwitches, next);
|
||||
|
||||
// 🔥 aktualizuj globalny stan
|
||||
window.fuzSwitchState = {
|
||||
selected: next,
|
||||
labels,
|
||||
@@ -115,7 +112,6 @@ export default function OffersSwitches(props) {
|
||||
}
|
||||
};
|
||||
|
||||
// 🔥 CONTROLLED: zsynchronizuj globalny stan + event
|
||||
useEffect(() => {
|
||||
if (!isControlled) return;
|
||||
if (!Array.isArray(switches) || !switches.length) return;
|
||||
@@ -123,7 +119,6 @@ export default function OffersSwitches(props) {
|
||||
const safeSelected = selected || {};
|
||||
const labels = buildLabels(switches, safeSelected);
|
||||
|
||||
// 🔥 globalny stan
|
||||
window.fuzSwitchState = {
|
||||
selected: safeSelected,
|
||||
labels,
|
||||
@@ -162,23 +157,20 @@ export default function OffersSwitches(props) {
|
||||
return (
|
||||
<div class="f-switches-wrapper">
|
||||
{effectiveSwitches.map((sw) => (
|
||||
<div class="f-switch-box">
|
||||
<div class="f-switch-group">
|
||||
{sw.opcje.map((op) => (
|
||||
<button
|
||||
type="button"
|
||||
class={`f-switch ${
|
||||
String(effectiveSelected[sw.id]) === String(op.id)
|
||||
? "active"
|
||||
: ""
|
||||
<div class="f-switch-group">
|
||||
{sw.opcje.map((op) => (
|
||||
<button
|
||||
type="button"
|
||||
class={`f-switch ${String(effectiveSelected[sw.id]) === String(op.id)
|
||||
? "active"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() => handleClick(sw.id, op.id)}
|
||||
title={sw.title}
|
||||
>
|
||||
{op.nazwa}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
onClick={() => handleClick(sw.id, op.id)}
|
||||
title={sw.title}
|
||||
>
|
||||
{op.nazwa}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -79,20 +79,15 @@ export default function JamboxChannelsModal({ isOpen, onClose, pkg }) {
|
||||
<div class="max-w-8xl mx-auto px-3 md:px-6">
|
||||
<h2 class="fuz-modal-title">Kanały w pakiecie {pkg.name}</h2>
|
||||
|
||||
<div class="jmb-search">
|
||||
<div class="fuz-chsearch__top">
|
||||
<input
|
||||
class="jmb-search-input"
|
||||
class="fuz-chsearch__input"
|
||||
type="search"
|
||||
value={query}
|
||||
onInput={(e) => setQuery(e.currentTarget.value)}
|
||||
placeholder="Szukaj kanału po nazwie…"
|
||||
aria-label="Szukaj kanału po nazwie"
|
||||
/>
|
||||
{query && (
|
||||
<button class="jmb-search-clear" type="button" onClick={() => setQuery("")}>
|
||||
Wyczyść
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!loading && !error && (
|
||||
@@ -112,35 +107,32 @@ export default function JamboxChannelsModal({ isOpen, onClose, pkg }) {
|
||||
<div class="">
|
||||
<div class="f-section-channel">
|
||||
{filtered.map((ch) => (
|
||||
<div class="jmb-channel-card" key={ch.number}>
|
||||
<div
|
||||
class="jmb-channel-card"
|
||||
key={ch.number}
|
||||
onClick={(e) => {
|
||||
if (e.target.closest("a, button")) return;
|
||||
e.currentTarget.classList.toggle("is-flipped");
|
||||
}}
|
||||
>
|
||||
<div class="jmb-channel-inner">
|
||||
{/* FRONT */}
|
||||
<div class="jmb-channel-face jmb-channel-front">
|
||||
{ch.logo_url && (
|
||||
<img
|
||||
src={ch.logo_url}
|
||||
alt={ch.name}
|
||||
class="jmb-channel-logo"
|
||||
loading="lazy"
|
||||
/>
|
||||
<img src={ch.logo_url} alt={ch.name} class="jmb-channel-logo" loading="lazy" />
|
||||
)}
|
||||
<div class="jmb-channel-name">{ch.name}</div>
|
||||
<div class="jmb-channel-number">kanał {ch.number}</div>
|
||||
</div>
|
||||
|
||||
{/* BACK */}
|
||||
<div class="jmb-channel-face jmb-channel-back">
|
||||
<div class="jmb-channel-back-title">{ch.name}</div>
|
||||
<div
|
||||
class="jmb-channel-desc"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: ch.description || "<em>Brak opisu kanału.</em>",
|
||||
}}
|
||||
dangerouslySetInnerHTML={{ __html: ch.description || "<em>Brak opisu kanału.</em>" }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -97,9 +97,9 @@ export default function JamboxChannelsSearch() {
|
||||
{c.name}
|
||||
</div>
|
||||
|
||||
<div class="fuz-chsearch__channel-number">
|
||||
{/* <div class="fuz-chsearch__channel-number">
|
||||
kanał {c.min_number || "—"}
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ export default function JamboxChannelsSearch() {
|
||||
{c.packages.map((p, i) => (
|
||||
<span class="fuz-chsearch__pkg" key={p.id}>
|
||||
{p.name}{" "}
|
||||
<span class="fuz-chsearch__pkgnum">({p.number})</span>
|
||||
<span class="fuz-chsearch__pkgnum">(kanał {p.number})</span>
|
||||
{i < c.packages.length - 1 ? ", " : ""}
|
||||
</span>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user