diff --git a/src/components/TrocardePerfis.jsx b/src/components/TrocardePerfis.jsx index d340b7f..f95b3fd 100644 --- a/src/components/TrocardePerfis.jsx +++ b/src/components/TrocardePerfis.jsx @@ -46,7 +46,7 @@ const TrocardePerfis = () => { value={selectedProfile} onChange={handleSelectChange} > - Selecionar perfil + Selecionar perfil {options.map((opt) => ( {opt.label} diff --git a/src/components/botaoacessibilidade.css b/src/components/botaoacessibilidade.css index 7046e09..a2a0a8e 100644 --- a/src/components/botaoacessibilidade.css +++ b/src/components/botaoacessibilidade.css @@ -1,272 +1,203 @@ -/* --- ESTILO PARA ESCONDER O BOTÃO ORIGINAL DO VLIBRAS --- */ +@import url('https://fonts.cdnfonts.com/css/open-dyslexic'); + [vw-access-button] { display: none !important; } -/* --- ESTILOS GERAIS DO COMPONENTE --- */ .container-acessibilidade { position: fixed; bottom: 20px; right: 20px; - z-index: 99998; - display: flex; - flex-direction: column; - align-items: center; - pointer-events: none; /* Impede cliques no contêiner */ + z-index: 10000; } - .botao-flutuante-acessibilidade { - position: relative; - z-index: 2; /* Acima do menu */ - background: linear-gradient(45deg, #007bff, #0056b3); - color: white; - border: none; - border-radius: 50%; width: 60px; height: 60px; + border-radius: 50%; + background-color: #007bff; + color: white; + border: none; + box-shadow: 0 4px 8px rgba(0,0,0,0.2); cursor: pointer; - box-shadow: 0 5px 15px rgba(0, 91, 179, 0.4); display: flex; - justify-content: center; align-items: center; - transition: transform 0.2s ease-in-out, box-shadow 0.2s ease; - margin-top: 15px; /* Distância do menu */ - pointer-events: auto; /* Permite que o botão seja clicável */ + justify-content: center; } - -.botao-flutuante-acessibilidade:hover { - transform: scale(1.1); - box-shadow: 0 8px 20px rgba(0, 91, 179, 0.5); -} - -/* --- ESTILOS DO MENU "BALÃO" --- */ .menu-opcoes { - background-color: #ffffff; - border-radius: 12px; - box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1); - padding: 8px; + position: absolute; + bottom: 70px; + right: 0; width: 280px; - z-index: 1; /* Abaixo do botão principal */ - border: 1px solid #e9ecef; - - /* Animação */ - transform-origin: bottom center; - transform: translateY(10px) scale(0.95); + max-height: calc(100vh - 100px); + overflow-y: auto; + background-color: white; + border-radius: 8px; + box-shadow: 0 6px 12px rgba(0,0,0,0.3); + display: flex; + flex-direction: column; + gap: 8px; + padding: 15px; + transform: scale(0.95); + transform-origin: bottom right; opacity: 0; visibility: hidden; - transition: all 0.2s ease; - pointer-events: auto; /* Permite que o menu seja clicável */ + transition: transform 0.2s ease, opacity 0.2s ease, visibility 0.2s; } - .menu-opcoes.aberto { - transform: translateY(0) scale(1); + transform: scale(1); opacity: 1; visibility: visible; } - .menu-titulo { - font-size: 14px; - font-weight: 600; - color: #6c757d; - padding: 8px 12px; - border-bottom: 1px solid #f1f3f5; - margin-bottom: 5px; - transition: color 0.2s ease, border-bottom-color 0.2s ease; + font-size: 18px; + font-weight: bold; + margin-bottom: 10px; + text-align: center; + color: #333; } - -/* --- ESTILOS DOS BOTÕES E DA CHECKBOX NO MENU --- */ .menu-opcoes button, .checkbox-label-button { + width: 100%; + padding: 12px; + border-radius: 6px; + border: 1px solid #ddd; + background-color: #f9f9f9; + cursor: pointer; + text-align: left; display: flex; align-items: center; - gap: 12px; - background-color: transparent; - border: none; - padding: 12px; - text-align: left; - cursor: pointer; - font-size: 16px; - color: #212529; - width: 100%; - border-radius: 8px; - transition: background-color 0.2s ease, color 0.2s ease; + gap: 10px; + font-size: 15px; + color: #333; + transition: background-color 0.2s; } - .menu-opcoes button:hover, .checkbox-label-button:hover { - background-color: #f8f9fa; + background-color: #f0f0f0; } - -/* --- ESTILO DO INTERRUPTOR (CHECKBOX) --- */ -.checkbox-label-button { - justify-content: space-between; -} - .checkbox-label-button input[type="checkbox"] { - appearance: none; - -webkit-appearance: none; - position: relative; - width: 44px; - height: 24px; - background-color: #ced4da; - border-radius: 12px; - cursor: pointer; - transition: background-color 0.3s ease-in-out; -} - -.checkbox-label-button input[type="checkbox"]::before { - content: ''; - position: absolute; - top: 2px; - left: 2px; + margin-left: auto; width: 20px; height: 20px; - background-color: white; - border-radius: 50%; - box-shadow: 0 1px 3px rgba(0,0,0,0.2); - transition: transform 0.3s ease-in-out; + cursor: pointer; } -.checkbox-label-button input[type="checkbox"]:checked { - background-color: #0d6efd; -} - -.checkbox-label-button input[type="checkbox"]:checked::before { - transform: translateX(20px); -} - - -/* --- ✨ NOVOS ESTILOS PARA O CONTROLE DE FONTE ✨ --- */ - +/* Tamanho da Fonte */ .font-size-control { display: flex; - align-items: center; - justify-content: space-between; - padding: 8px 12px; - color: #212529; - transition: color 0.2s ease; - border-top: 1px solid #f1f3f5; - margin-top: 5px; + flex-direction: column; + padding: 10px; + background-color: rgba(0, 0, 0, 0.05); + border-radius: 8px; + margin-bottom: 5px; + gap: 10px; } - .font-size-label { - font-size: 16px; - display: flex; - align-items: center; - gap: 12px; -} - -.font-size-buttons { display: flex; align-items: center; gap: 8px; -} - -.font-size-buttons button { - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; - font-weight: bold; - font-size: 16px; - background-color: #e9ecef; - color: #495057; - border: 1px solid #dee2e6; - border-radius: 6px; - width: 36px; - height: 32px; - padding: 0; - cursor: pointer; - display: flex; - justify-content: center; - align-items: center; - transition: background-color 0.2s ease, color 0.2s ease; -} - -.font-size-buttons button:hover { - background-color: #dee2e6; -} - -.font-size-buttons button:disabled { - background-color: #f8f9fa; - color: #adb5bd; - cursor: not-allowed; - border-color: #f1f3f5; -} - -.font-size-display { - font-size: 14px; font-weight: 600; - color: #495057; - min-width: 45px; + font-size: 15px; +} +.font-size-buttons { + display: flex; + justify-content: space-between; + align-items: center; +} +.font-size-buttons button { + border-radius: 50%; + width: 38px; + height: 38px; + border: 1px solid #ddd; + background-color: #fff; + font-size: 16px; + font-weight: bold; + cursor: pointer; + color: #333; + display: flex; + align-items: center; + justify-content: center; + padding: 0; +} +.font-size-buttons span { + font-size: 16px; + font-weight: 600; + min-width: 50px; text-align: center; - transition: color 0.2s ease; } -/* Dark mode styles */ -html[data-bs-theme="dark"] { - - /* Floating button */ - .botao-flutuante-acessibilidade { - background: linear-gradient(45deg, #212529, #343a40); - color: #f8f9fa; - box-shadow: 0 5px 15px rgba(33, 37, 41, 0.4); - } - .botao-flutuante-acessibilidade:hover { - box-shadow: 0 8px 20px rgba(33, 37, 41, 0.5); - } - - /* Menu balloon */ - .menu-opcoes { - background-color: #23272b; - border: 1px solid #343a40; - box-shadow: 0 8px 25px rgba(0,0,0,0.4); - } - .menu-titulo { - color: #adb5bd; - border-bottom: 1px solid #343a40; - } - - /* Menu buttons and checkbox */ - .menu-opcoes button, - .checkbox-label-button { - color: #f8f9fa; - } - .menu-opcoes button:hover, - .checkbox-label-button:hover { - background-color: #343a40; - } - - /* Checkbox switch */ - .checkbox-label-button input[type="checkbox"] { - background-color: #495057; - } - .checkbox-label-button input[type="checkbox"]:checked { - background-color: #0d6efd; - } - .checkbox-label-button input[type="checkbox"]::before { - background-color: #f8f9fa; - } - - /* Font size control */ - .font-size-control { - color: #f8f9fa; - border-top: 1px solid #343a40; - } - .font-size-label { - color: #f8f9fa; - } - .font-size-buttons button { - background-color: #343a40; - color: #f8f9fa; - border: 1px solid #495057; - } - .font-size-buttons button:hover { - background-color: #495057; - } - .font-size-buttons button:disabled { - background-color: #23272b; - color: #6c757d; - border-color: #343a40; - } - .font-size-display { - color: #f8f9fa; - } +/* Fonte para Dislexia */ +.dyslexia-font-active * { + font-family: 'Open-Dyslexic', sans-serif !important; } + +/* Espaçamento e Altura */ +.letter-spacing-active p, .letter-spacing-active li, .letter-spacing-active span, .letter-spacing-active a, .letter-spacing-active div, .letter-spacing-active td, .letter-spacing-active h1, .letter-spacing-active h2, .letter-spacing-active h3, .letter-spacing-active h4, .letter-spacing-active h5, .letter-spacing-active h6 { + letter-spacing: 1.5px !important; +} +.line-height-active p, .line-height-active li, .line-height-active span, .line-height-active a, .line-height-active div, .line-height-active td, .line-height-active h1, .line-height-active h2, .line-height-active h3, .line-height-active h4, .line-height-active h5, .line-height-active h6 { + line-height: 3 !important; +} + +/* Guia de Leitura */ +.reading-guide-mask { + position: fixed; + left: 0; + width: 100%; + background: rgba(0, 0, 0, 0.7); + z-index: 99999; + pointer-events: none; + transition: all 0.05s ease-out; +} +.reading-guide-top { + top: 0; + height: 0; +} +.reading-guide-bottom { + bottom: 0; + top: 100vh; +} + +/* Cursor Grande */ +.big-cursor-active, .big-cursor-active * { + cursor: url('data:image/svg+xml;utf8,') 1 1, auto !important; +} + +/* Filtros de Daltonismo */ +.colorblind-protanopia { filter: url(#protanopia); } +.colorblind-deuteranopia { filter: url(#deuteranopia); } +.colorblind-tritanopia { filter: url(#tritanopia); } +.colorblind-achromatopsia { filter: url(#achromatopsia); } + +.acessibilidade-select-control { + display: flex; + flex-direction: column; + gap: 8px; + background-color: rgba(0, 0, 0, 0.05); + padding: 10px; + border-radius: 8px; + margin-bottom: 5px; +} +.acessibilidade-select-control label { + display: flex; + align-items: center; + gap: 8px; + font-weight: 600; + font-size: 15px; +} +.acessibilidade-select-control select { + width: 100%; + padding: 8px; + border-radius: 4px; + border: 1px solid #ccc; + font-size: 14px; +} + +/* Dark Mode */ +.dark-mode .menu-opcoes { background-color: #2d2d2d; color: #f1f1f1; } +.dark-mode .menu-titulo { color: #f1f1f1; } +.dark-mode .menu-opcoes button, .dark-mode .checkbox-label-button { background-color: #424242; border-color: #555; color: #f1f1f1; } +.dark-mode .menu-opcoes button:hover, .dark-mode .checkbox-label-button:hover { background-color: #535353; } +.dark-mode .font-size-control, .dark-mode .acessibilidade-select-control { background-color: rgba(255, 255, 255, 0.1); } +.dark-mode .font-size-buttons button { background-color: #535353; color: #f1f1f1; border-color: #666; } +.dark-mode .acessibilidade-select-control select { background-color: #535353; color: #f1f1f1; border-color: #666; } \ No newline at end of file diff --git a/src/components/botaoacessibilidade.jsx b/src/components/botaoacessibilidade.jsx index 02a564d..2a8c60f 100644 --- a/src/components/botaoacessibilidade.jsx +++ b/src/components/botaoacessibilidade.jsx @@ -2,41 +2,131 @@ import React, { useState, useEffect, useRef } from 'react'; import './botaoacessibilidade.css'; // Importando o CSS import { setTheme } from '../assets/static/js/components/dark'; +// Componente para o Guia de Leitura +function GuiaDeLeitura() { + const topMaskRef = useRef(null); + const bottomMaskRef = useRef(null); + useEffect(() => { + const handleMouseMove = (e) => { + if (topMaskRef.current && bottomMaskRef.current) { + const windowHeight = 40; + const offset = windowHeight / 2; + topMaskRef.current.style.height = `${e.clientY - offset}px`; + bottomMaskRef.current.style.top = `${e.clientY + offset}px`; + } + }; + window.addEventListener('mousemove', handleMouseMove); + return () => { window.removeEventListener('mousemove', handleMouseMove); }; + }, []); + + return ( + <> + + + > + ); +} + +// --- COMPONENTE PRINCIPAL --- function BotaoAcessibilidade() { + // Estados de todas as funcionalidades const [isMenuOpen, setIsMenuOpen] = useState(false); - const [isReadOnHoverActive, setIsReadOnHoverActive] = useState(false); const [isDarkMode, setIsDarkMode] = useState(false); + const [isReadOnHoverActive, setIsReadOnHoverActive] = useState(false); + const [fontSize, setFontSize] = useState(1); + const [isBigCursor, setIsBigCursor] = useState(false); + const [colorblindMode, setColorblindMode] = useState('none'); + const [isReadingGuide, setIsReadingGuide] = useState(false); + const [isDyslexiaFont, setIsDyslexiaFont] = useState(false); + const [isLetterSpacing, setIsLetterSpacing] = useState(false); + const [isLineHeight, setIsLineHeight] = useState(false); const lastSpokenTargetRef = useRef(null); + // Efeitos para aplicar as funcionalidades na página + useEffect(() => { setTheme(isDarkMode ? "dark" : "light", true); }, [isDarkMode]); useEffect(() => { - setTheme(isDarkMode ? "dark" : "light", true); - }, [isDarkMode]); - + const originalFontSize = document.documentElement.style.fontSize; + document.documentElement.style.fontSize = `${fontSize * 100}%`; + return () => { document.documentElement.style.fontSize = originalFontSize; }; + }, [fontSize]); useEffect(() => { - if (!isReadOnHoverActive) { - window.speechSynthesis.cancel(); - return; + document.body.classList.toggle('big-cursor-active', isBigCursor); + return () => { document.body.classList.remove('big-cursor-active'); }; + }, [isBigCursor]); + useEffect(() => { + document.body.classList.toggle('dyslexia-font-active', isDyslexiaFont); + return () => { document.body.classList.remove('dyslexia-font-active'); }; + }, [isDyslexiaFont]); + useEffect(() => { + document.body.classList.toggle('letter-spacing-active', isLetterSpacing); + return () => { document.body.classList.remove('letter-spacing-active'); }; + }, [isLetterSpacing]); + useEffect(() => { + document.body.classList.toggle('line-height-active', isLineHeight); + return () => { document.body.classList.remove('line-height-active'); }; + }, [isLineHeight]); + useEffect(() => { + const classesToRemove = ['colorblind-protanopia', 'colorblind-deuteranopia', 'colorblind-tritanopia', 'colorblind-achromatopsia']; + document.documentElement.classList.remove(...classesToRemove); + if (colorblindMode !== 'none') { + document.documentElement.classList.add(`colorblind-${colorblindMode}`); } - const handleMouseOver = (event) => { - const target = event.target; - if (target && target !== lastSpokenTargetRef.current && target.innerText) { - const text = target.innerText.trim(); - if (text.length > 0 && ['P', 'H1', 'H2', 'H3', 'BUTTON', 'A', 'LI', 'LABEL'].includes(target.tagName)) { - lastSpokenTargetRef.current = target; + }, [colorblindMode]); +// VERSÃO DEFINITIVA - Muito mais abrangente +useEffect(() => { + if (!isReadOnHoverActive) { + window.speechSynthesis.cancel(); + return; + } + + const handleMouseOver = (event) => { + // Seletor muito mais completo, incluindo DIVs, SPANs, células de tabela, imagens e papéis de acessibilidade + const selector = 'P, H1, H2, H3, H4, H5, H6, BUTTON, A, LI, LABEL, SPAN, TD, TH, DT, DD, FIGCAPTION, [role="button"], [role="link"], [role="menuitem"], IMG'; + const relevantElement = event.target.closest(selector); + + if (relevantElement && relevantElement !== lastSpokenTargetRef.current) { + let textToSpeak = ''; + + // Lógica especial para IMAGENS: lê o atributo "alt" + if (relevantElement.tagName === 'IMG') { + textToSpeak = relevantElement.getAttribute('alt'); + } + // Lógica para outros elementos: prioriza aria-label, depois title, e por último o texto interno + else { + textToSpeak = + relevantElement.getAttribute('aria-label') || + relevantElement.getAttribute('title') || + relevantElement.innerText; + } + + if (textToSpeak) { + textToSpeak = textToSpeak.trim(); + + // Evita ler elementos que só têm elementos filhos, mas nenhum texto próprio direto + const hasTextAndNoChildren = textToSpeak.length > 0 && relevantElement.children.length === 0; + const hasTextAndIsBlock = textToSpeak.length > 0 && !['SPAN', 'A', 'STRONG', 'EM'].includes(relevantElement.tagName); + + if (hasTextAndNoChildren || hasTextAndIsBlock) { + lastSpokenTargetRef.current = relevantElement; window.speechSynthesis.cancel(); - const utterance = new SpeechSynthesisUtterance(text); + const utterance = new SpeechSynthesisUtterance(textToSpeak); utterance.lang = 'pt-BR'; window.speechSynthesis.speak(utterance); } } - }; - document.body.addEventListener('mouseover', handleMouseOver); - return () => { - document.body.removeEventListener('mouseover', handleMouseOver); - window.speechSynthesis.cancel(); - }; - }, [isReadOnHoverActive]); + } + }; + document.body.addEventListener('mouseover', handleMouseOver); + return () => { + document.body.removeEventListener('mouseover', handleMouseOver); + window.speechSynthesis.cancel(); + }; +}, [isReadOnHoverActive]); + + // Funções de controle + const handleIncreaseFontSize = () => setFontSize(prevSize => Math.min(prevSize + 0.1, 1.6)); + const handleDecreaseFontSize = () => setFontSize(prevSize => Math.max(prevSize - 0.1, 0.8)); const handleVlibrasClick = () => { const originalVlibrasButton = document.querySelector('[vw-access-button]'); if (originalVlibrasButton) { @@ -47,53 +137,96 @@ function BotaoAcessibilidade() { setIsMenuOpen(false); }; - const handleReadAloud = () => { - const selectedText = window.getSelection().toString().trim(); - if (selectedText) { - window.speechSynthesis.cancel(); - const utterance = new SpeechSynthesisUtterance(selectedText); - utterance.lang = 'pt-BR'; - window.speechSynthesis.speak(utterance); - } else { - alert("Por favor, selecione um texto para ler em voz alta."); - } - setIsMenuOpen(false); - }; - return ( + {isReadingGuide && } + + + + + + + + Acessibilidade + + + + + Tamanho da Fonte + + + A- + {`${Math.round(fontSize * 100)}%`} + A+ + + + + + + + Modo Daltonismo + + setColorblindMode(e.target.value)}> + Desativado + Protanopia + Deuteranopia + Tritanopia + Monocromático + + + Modo Escuro - setIsDarkMode(!isDarkMode)} - /> + setIsDarkMode(!isDarkMode)} /> + + + + Fonte para Dislexia + setIsDyslexiaFont(!isDyslexiaFont)} /> + + + + + Espaçamento entre Letras + setIsLetterSpacing(!isLetterSpacing)} /> + + + + + Altura da Linha + setIsLineHeight(!isLineHeight)} /> + + + + + Guia de Leitura + setIsReadingGuide(!isReadingGuide)} /> + + + + + Cursor Grande + setIsBigCursor(!isBigCursor)} /> + + Leitura instantânea - setIsReadOnHoverActive(!isReadOnHoverActive)} - /> + setIsReadOnHoverActive(!isReadOnHoverActive)} /> + + {/* ADICIONADO DE VOLTA: Botão para LIBRAS */} - - Traduzir para LIBRAS + + Traduzir para LIBRAS - setIsMenuOpen(!isMenuOpen)} - title="Menu de Acessibilidade" - > + + setIsMenuOpen(!isMenuOpen)} title="Menu de Acessibilidade"> @@ -101,6 +234,4 @@ function BotaoAcessibilidade() { ); } - -export default BotaoAcessibilidade; - +export default BotaoAcessibilidade; \ No newline at end of file