|
|
| import { useState, useEffect } from 'react'; |
| import { Type, Sun, MonitorOff, RotateCcw, Accessibility, Eye, Volume2 } from 'lucide-react'; |
| import { Button } from "@/components/ui/button"; |
| import { |
| DropdownMenu, |
| DropdownMenuContent, |
| DropdownMenuLabel, |
| DropdownMenuSeparator, |
| DropdownMenuTrigger, |
| } from "@/components/ui/dropdown-menu"; |
| import { useTranslation } from 'react-i18next'; |
|
|
| const AccessibilityMenu = () => { |
| const { i18n } = useTranslation(); |
| const [fontSize, setFontSize] = useState(100); |
| const [highContrast, setHighContrast] = useState(false); |
| const [grayscale, setGrayscale] = useState(false); |
| const [readableFont, setReadableFont] = useState(false); |
| const [screenReader, setScreenReader] = useState(false); |
|
|
| |
| useEffect(() => { |
| const root = document.documentElement; |
| |
| |
| root.style.fontSize = `${fontSize}%`; |
| |
| |
| if (highContrast) { |
| root.classList.add('high-contrast'); |
| document.body.style.backgroundColor = '#000'; |
| document.body.style.color = '#fff'; |
| |
| const cards = document.querySelectorAll('.bg-white'); |
| cards.forEach((el: any) => { |
| el.style.backgroundColor = '#1a1a1a'; |
| el.style.color = '#fff'; |
| el.style.borderColor = '#333'; |
| }); |
| } else { |
| root.classList.remove('high-contrast'); |
| document.body.style.backgroundColor = ''; |
| document.body.style.color = ''; |
| const cards = document.querySelectorAll('.bg-white'); |
| cards.forEach((el: any) => { |
| el.style.backgroundColor = ''; |
| el.style.color = ''; |
| el.style.borderColor = ''; |
| }); |
| } |
|
|
| |
| if (grayscale) { |
| root.style.filter = 'grayscale(100%)'; |
| } else { |
| root.style.filter = ''; |
| } |
|
|
| |
| if (readableFont) { |
| document.body.classList.add('readable-font'); |
| document.body.style.fontFamily = 'Arial, Helvetica, sans-serif'; |
| document.body.style.lineHeight = '1.8'; |
| document.body.style.letterSpacing = '0.05em'; |
| } else { |
| document.body.classList.remove('readable-font'); |
| document.body.style.fontFamily = ''; |
| document.body.style.lineHeight = ''; |
| document.body.style.letterSpacing = ''; |
| } |
|
|
| }, [fontSize, highContrast, grayscale, readableFont]); |
|
|
| |
| useEffect(() => { |
| if (!screenReader) { |
| window.speechSynthesis.cancel(); |
| return; |
| } |
|
|
| const handleMouseOver = (e: MouseEvent) => { |
| const target = e.target as HTMLElement; |
| if (target && (target.innerText || target.getAttribute('aria-label'))) { |
| window.speechSynthesis.cancel(); |
| const text = target.getAttribute('aria-label') || target.innerText; |
| if (text && text.trim().length > 0) { |
| |
| if (text.length < 200) { |
| const utterance = new SpeechSynthesisUtterance(text); |
| |
| utterance.lang = i18n.language === 'hi' ? 'hi-IN' : 'en-US'; |
| window.speechSynthesis.speak(utterance); |
| } |
| } |
| } |
| }; |
|
|
| document.addEventListener('mouseover', handleMouseOver); |
| return () => { |
| document.removeEventListener('mouseover', handleMouseOver); |
| window.speechSynthesis.cancel(); |
| }; |
| }, [screenReader, i18n.language]); |
|
|
| const resetSettings = () => { |
| setFontSize(100); |
| setHighContrast(false); |
| setGrayscale(false); |
| setReadableFont(false); |
| setScreenReader(false); |
| }; |
|
|
| return ( |
| <div className="fixed bottom-6 right-6 z-[100]"> |
| <DropdownMenu> |
| <DropdownMenuTrigger asChild> |
| <Button |
| size="icon" |
| className="h-14 w-14 rounded-full shadow-2xl bg-blue-600 hover:bg-blue-700 text-white border-4 border-white" |
| aria-label="Accessibility Options" |
| > |
| <Accessibility className="h-8 w-8" /> |
| </Button> |
| </DropdownMenuTrigger> |
| <DropdownMenuContent align="end" className="w-72 p-4 bg-white/95 backdrop-blur-sm shadow-xl border-blue-100"> |
| <DropdownMenuLabel className="flex items-center justify-between text-lg"> |
| <span>Accessibility Tools</span> |
| <Button variant="ghost" size="sm" onClick={resetSettings} className="h-8 px-2 text-xs text-red-500 hover:text-red-700 hover:bg-red-50"> |
| <RotateCcw className="h-3 w-3 mr-1" /> Reset |
| </Button> |
| </DropdownMenuLabel> |
| <DropdownMenuSeparator /> |
| |
| {/* Font Size Control */} |
| <div className="py-3"> |
| <div className="text-sm font-medium mb-3 flex items-center text-gray-700"> |
| <Type className="h-4 w-4 mr-2" /> Text Size ({fontSize}%) |
| </div> |
| <div className="flex gap-2"> |
| <Button |
| variant="outline" |
| size="sm" |
| className="flex-1 border-gray-200 hover:bg-blue-50 hover:border-blue-200 hover:text-blue-600" |
| onClick={() => setFontSize(Math.max(80, fontSize - 10))} |
| disabled={fontSize <= 80} |
| > |
| A- |
| </Button> |
| <Button |
| variant="outline" |
| size="sm" |
| className="flex-1 border-gray-200 hover:bg-blue-50 hover:border-blue-200 hover:text-blue-600" |
| onClick={() => setFontSize(Math.min(150, fontSize + 10))} |
| disabled={fontSize >= 150} |
| > |
| A+ |
| </Button> |
| </div> |
| </div> |
| |
| <DropdownMenuSeparator className="my-1"/> |
| |
| {/* Display Options */} |
| <div className="space-y-2 py-2"> |
| <Button |
| variant={highContrast ? "default" : "ghost"} |
| className={`w-full justify-start ${highContrast ? 'bg-black text-white hover:bg-gray-800' : 'hover:bg-gray-100 text-gray-700'}`} |
| onClick={() => setHighContrast(!highContrast)} |
| > |
| <Sun className="h-4 w-4 mr-3" /> |
| {highContrast ? 'Disable High Contrast' : 'High Contrast Mode'} |
| </Button> |
| |
| <Button |
| variant={grayscale ? "default" : "ghost"} |
| className={`w-full justify-start ${grayscale ? 'bg-gray-600 hover:bg-gray-700' : 'hover:bg-gray-100 text-gray-700'}`} |
| onClick={() => setGrayscale(!grayscale)} |
| > |
| <MonitorOff className="h-4 w-4 mr-3" /> |
| {grayscale ? 'Disable Grayscale' : 'Grayscale Mode'} |
| </Button> |
| |
| <Button |
| variant={readableFont ? "default" : "ghost"} |
| className={`w-full justify-start ${readableFont ? 'bg-blue-600 hover:bg-blue-700' : 'hover:bg-gray-100 text-gray-700'}`} |
| onClick={() => setReadableFont(!readableFont)} |
| > |
| <Eye className="h-4 w-4 mr-3" /> |
| {readableFont ? 'Disable Readable Font' : 'Dyslexia Friendly Font'} |
| </Button> |
| |
| <Button |
| variant={screenReader ? "default" : "ghost"} |
| className={`w-full justify-start ${screenReader ? 'bg-green-600 hover:bg-green-700' : 'hover:bg-gray-100 text-gray-700'}`} |
| onClick={() => setScreenReader(!screenReader)} |
| > |
| <Volume2 className="h-4 w-4 mr-3" /> |
| {screenReader ? 'Turn Off Screen Reader' : 'Screen Reader (Hover)'} |
| </Button> |
| </div> |
| </DropdownMenuContent> |
| </DropdownMenu> |
| </div> |
| ); |
| }; |
|
|
| export default AccessibilityMenu; |
|
|