Browse Source

fix: enhance modal accessibility, improve audio settings, and update sidebar translations

master
sina_sajjadi 3 weeks ago
parent
commit
d94b25f693
  1. 12
      next.config.ts
  2. 15
      src/components/modals/audio-setting.tsx
  3. 52
      src/components/modals/reciters.tsx
  4. 2
      src/components/modals/setting.tsx
  5. 68
      src/components/sidebar/list.tsx
  6. 2
      src/components/sidebar/tabs.tsx

12
next.config.ts

@ -7,12 +7,12 @@ const nextConfig: NextConfig = {
images: { images: {
domains: ["habibapp.com"], // Add the domain for image hosting domains: ["habibapp.com"], // Add the domain for image hosting
}, },
typescript : {
ignoreBuildErrors : true
},
eslint : {
ignoreDuringBuilds : true
}
// typescript : {
// ignoreBuildErrors : true
// },
// eslint : {
// ignoreDuringBuilds : true
// }
// Add other Next.js config options here as needed // Add other Next.js config options here as needed
}; };

15
src/components/modals/audio-setting.tsx

@ -33,14 +33,25 @@ const AudioSetting: React.FC<ModalProps> = ({ className = "" }) => {
audioRef.current.playbackRate = speed; // Update the audio playback speed audioRef.current.playbackRate = speed; // Update the audio playback speed
} }
}, [speed]); // Re-run this effect whenever speed changes }, [speed]); // Re-run this effect whenever speed changes
const modalClasses = windowWidth < 1024 ? "" : "max-w-96 bottom-20 right-0"; const modalClasses = windowWidth < 1024 ? "" : "max-w-96 bottom-20 right-0";
const increaseSpeed = () => { const increaseSpeed = () => {
setSpeed((prevSpeed) => (prevSpeed < 2 ? +(prevSpeed + 0.2).toFixed(1) : 1)); setSpeed((prevSpeed) => (prevSpeed < 2 ? +(prevSpeed + 0.2).toFixed(1) : 1));
}; };
const handleClickOutside = (event: MouseEvent) => {
if (modalRef.current && !modalRef.current.contains(event.target as Node)) {
closeModal(); // Close the modal if clicked outside
}
};
useEffect(() => {
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);
return ( return (
<div <div

52
src/components/modals/reciters.tsx

@ -17,18 +17,15 @@ const RecitersModal: React.FC<ModalProps> = ({ className = "" }) => {
const modalRef = useRef<HTMLDivElement>(null); const modalRef = useRef<HTMLDivElement>(null);
const closeButtonRef = useRef<HTMLButtonElement>(null); const closeButtonRef = useRef<HTMLButtonElement>(null);
const previouslyFocusedElement = useRef<HTMLElement | null>(null); const previouslyFocusedElement = useRef<HTMLElement | null>(null);
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
const [windowWidth, setWindowWidth] = useState(typeof window !== "undefined" ? window.innerWidth : 0);
const [reciters, setReciters] = useState([]); const [reciters, setReciters] = useState([]);
const params = useParams(); const params = useParams();
const slug = params?.slug as string; const slug = params?.slug as string;
const id = slug.split("-").pop(); const id = slug.split("-").pop();
const modalClasses = windowWidth < 1024 ? "" : "max-w-96 bottom-20 right-0";
console.log(windowWidth);
const modalClasses = windowWidth < 1024 ? "" : "max-w-96 bottom-20 right-0";
useEffect(() => { useEffect(() => {
http.get(`web/mafatih/${id}/reciters/`).then((res) => { http.get(`web/mafatih/${id}/reciters/`).then((res) => {
console.log("fdasfdasfads", res);
setReciters(res.data.results); setReciters(res.data.results);
}); });
@ -38,18 +35,14 @@ const RecitersModal: React.FC<ModalProps> = ({ className = "" }) => {
window.addEventListener("resize", handleResize); window.addEventListener("resize", handleResize);
// Cleanup the event listener on component unmount
return () => { return () => {
window.removeEventListener("resize", handleResize); window.removeEventListener("resize", handleResize);
}; };
}, [id]); }, [id]);
// Handle modal visibility and accessibility
useEffect(() => { useEffect(() => {
const handleOutsideClick = (event: MouseEvent) => { const handleOutsideClick = (event: MouseEvent) => {
if (
modalRef.current &&
!modalRef.current.contains(event.target as Node)
) {
if (modalRef.current && !modalRef.current.contains(event.target as Node)) {
closeModal(); closeModal();
} }
}; };
@ -60,32 +53,24 @@ const RecitersModal: React.FC<ModalProps> = ({ className = "" }) => {
} }
}; };
document.addEventListener("mousedown", handleOutsideClick);
document.addEventListener("keydown", handleKeyDown);
// Save the previously focused element and set modal focus
if (displaySetting) { if (displaySetting) {
previouslyFocusedElement.current = document.activeElement as HTMLElement; previouslyFocusedElement.current = document.activeElement as HTMLElement;
document.addEventListener("mousedown", handleOutsideClick);
document.addEventListener("keydown", handleKeyDown);
// Prevent background scrolling
document.body.style.overflow = "hidden";
// Shift focus to close button
document.body.style.overflow = "hidden"; // Prevent background scrolling
closeButtonRef.current?.focus(); closeButtonRef.current?.focus();
} else {
document.removeEventListener("mousedown", handleOutsideClick);
document.removeEventListener("keydown", handleKeyDown);
document.body.style.overflow = "auto";
// Return focus to previously focused element
previouslyFocusedElement.current?.focus();
} }
return () => { return () => {
document.removeEventListener("mousedown", handleOutsideClick); document.removeEventListener("mousedown", handleOutsideClick);
document.removeEventListener("keydown", handleKeyDown); document.removeEventListener("keydown", handleKeyDown);
document.body.style.overflow = "auto";
document.body.style.overflow = "auto"; // Restore scrolling
previouslyFocusedElement.current?.focus(); // Restore focus
}; };
}, []);
console.log(reciters);
reciters.map((item) => {
console.log(item);
});
}, [displaySetting]);
return ( return (
<div <div
ref={modalRef} ref={modalRef}
@ -93,10 +78,13 @@ const RecitersModal: React.FC<ModalProps> = ({ className = "" }) => {
role="dialog" role="dialog"
aria-modal="true" aria-modal="true"
> >
<div className="flex justify-between items-center p-6 ">
<div className="flex justify-between items-center p-6">
<div className="text-[#8B8B8B] text-sm font-bold">Reciter</div> <div className="text-[#8B8B8B] text-sm font-bold">Reciter</div>
<div></div>
<button onClick={closeModal}>
<button
ref={closeButtonRef}
onClick={closeModal}
aria-label="Close modal"
>
<IoMdClose size={18} /> <IoMdClose size={18} />
</button> </button>
</div> </div>
@ -108,7 +96,7 @@ const RecitersModal: React.FC<ModalProps> = ({ className = "" }) => {
closeModal(); closeModal();
}} }}
key={reciter?.id} key={reciter?.id}
className="flex py-4 px-6 items-center gap-4 border-b hover:bg-[#EBEBEB] cursor-pointer"
className="flex py-4 px-6 items-center gap-4 border-b hover:bg-[#EBEBEB] cursor-pointer"
> >
<div <div
className={`w-4 h-4 rounded-full border border-black ${ className={`w-4 h-4 rounded-full border border-black ${

2
src/components/modals/setting.tsx

@ -24,10 +24,12 @@ const SettingModal: React.FC<ModalProps> = ({ className = "" }) => {
// Handle modal visibility and accessibility // Handle modal visibility and accessibility
useEffect(() => { useEffect(() => {
const handleOutsideClick = (event: MouseEvent) => { const handleOutsideClick = (event: MouseEvent) => {
console.log(modalRef.current , modalRef.current.contains(event.target as Node));
if ( if (
modalRef.current && modalRef.current &&
!modalRef.current.contains(event.target as Node) !modalRef.current.contains(event.target as Node)
) { ) {
if (modalView === "SETTING_VIEW") { if (modalView === "SETTING_VIEW") {
closeModal(); closeModal();
} else { } else {

68
src/components/sidebar/list.tsx

@ -16,45 +16,39 @@ import { Category, Dua, ListProps } from "../utils/types";
const List: React.FC<ListProps> = ({ tab, path, setPath, data, setData }) => { const List: React.FC<ListProps> = ({ tab, path, setPath, data, setData }) => {
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
const [currentDhikr, setCurrentDhikr] = useState<string>("");
const [currentTranslation, setCurrentTranslation] = useState<string>("");
const router = useRouter(); const router = useRouter();
const today = new Date(); const today = new Date();
const dayOfWeek = new Intl.DateTimeFormat("en-US", { const dayOfWeek = new Intl.DateTimeFormat("en-US", {
weekday: "long", weekday: "long",
}).format(today); }).format(today);
let locale : string ;
// Mapping of days of the week to their respective Dhikr phrases with vowels and English translations // Mapping of days of the week to their respective Dhikr phrases with vowels and English translations
const dhikrMap: { [key: string]: { dhikr: string; english: string } } = {
Saturday: {
dhikr: "یَا رَبِّ الْعالَمِین",
english: "O Lord of the Worlds",
},
Sunday: {
dhikr: "یَا ذَا الْجَلاَلِ وَ الْاِکْرَامِ",
english: "O Lord of Glory and Honor",
},
Monday: {
dhikr: "یَا قَاضِیَ الْحَاجَاتِ",
english: "O the Judge of needs",
},
Tuesday: {
dhikr: "یَا أَرْحَمَ الرَّاحِمِین",
english: "O Most Merciful of all givers",
},
Wednesday: {
dhikr: "یَا حَیُّ یَا قَیُّومُ",
english: "O Ever-Living, O Sustainer",
},
Thursday: {
dhikr: "لَا إِلٰهَ إِلَّا اللَّهُ الْمَلِکُ الْحَقُّ الْمُبِینُ",
english:
"There is no god but Allah, the Sovereign, the True, the Manifest",
},
Friday: {
dhikr: "اللّهُمّ صَلِّ عَلَى مُحَمَّدٍ وَ آلِ مُحَمَّدٍ",
english:
"O Allah, send blessings upon Muhammad and the family of Muhammad",
},
};
useEffect(() => {
locale = localStorage.getItem("locale") || "en";
if (tab === "Today") {
setLoading(true);
http
.get("web/mafatih-duas/dhikrs/?today=true")
.then((res) => {
const dhikrForToday = res.data.find(
(item: any) => item.day === dayOfWeek
);
if (dhikrForToday) {
setCurrentDhikr(dhikrForToday.text);
setCurrentTranslation(
dhikrForToday.translation[locale] || dhikrForToday.translation.en
);
}
setLoading(false);
})
.catch((error) => {
console.error("Error fetching Dhikr data:", error);
setLoading(false);
});
}
}, [tab, dayOfWeek, locale]);
const openCategory = (category: Category) => { const openCategory = (category: Category) => {
setData({ type: "children", data: category.children }); setData({ type: "children", data: category.children });
@ -138,15 +132,13 @@ const List: React.FC<ListProps> = ({ tab, path, setPath, data, setData }) => {
}); });
}); });
} }
// eslint-disable-next-line react-hooks/exhaustive-deps
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [path]); }, [path]);
if (loading) { if (loading) {
return <p>Loading ...</p>; return <p>Loading ...</p>;
} }
const currentDhikr = dhikrMap[dayOfWeek]?.dhikr || "";
const currentEnglish = dhikrMap[dayOfWeek]?.english || "";
console.log(data); console.log(data);
return ( return (
@ -174,7 +166,7 @@ const List: React.FC<ListProps> = ({ tab, path, setPath, data, setData }) => {
{colorizeVowels(currentDhikr)} {colorizeVowels(currentDhikr)}
</div> </div>
<p className="text-sm text-[#8B8B8B]">{currentEnglish}</p>
<p className="text-sm text-[#8B8B8B]">{currentTranslation}</p>
</div> </div>
</div> </div>
</div> </div>
@ -238,7 +230,7 @@ const List: React.FC<ListProps> = ({ tab, path, setPath, data, setData }) => {
)} )}
</div> </div>
); );
}
}
}) })
) : ( ) : (
<div className="flex my-[40%] items-center justify-center"> <div className="flex my-[40%] items-center justify-center">

2
src/components/sidebar/tabs.tsx

@ -86,7 +86,7 @@ const Tabs = () => {
<div className="rounded-2xl bg-[#EBEBEB]"> <div className="rounded-2xl bg-[#EBEBEB]">
{/* Render the path names */} {/* Render the path names */}
<ul className="p-1 flex gap-6 font-semibold text-xs whitespace-nowrap text-[#8B8B8B]">
<ul className="p-1 flex justify-between font-semibold text-xs whitespace-nowrap text-[#8B8B8B]">
{tabs.map((tab) => ( {tabs.map((tab) => (
<li <li
key={tab.name} key={tab.name}

Loading…
Cancel
Save