Compare commits
3 Commits
main
...
ChatSuport
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
024e730687 | ||
|
|
c9d1457775 | ||
|
|
b2e2423978 |
@ -23,6 +23,6 @@
|
|||||||
<!-- <script src="%PUBLIC_URL%/vendors/perfect-scrollbar/perfect-scrollbar.min.js"></script>
|
<!-- <script src="%PUBLIC_URL%/vendors/perfect-scrollbar/perfect-scrollbar.min.js"></script>
|
||||||
<script src="%PUBLIC_URL%/js/bootstrap.bundle.min.js"></script>
|
<script src="%PUBLIC_URL%/js/bootstrap.bundle.min.js"></script>
|
||||||
<script src="%PUBLIC_URL%/js/main.js"></script> -->
|
<script src="%PUBLIC_URL%/js/main.js"></script> -->
|
||||||
<script src="https://website-widgets.pages.dev/dist/sienna.min.js" defer></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
47
src/App.css
47
src/App.css
@ -44,4 +44,51 @@ html[data-bs-theme="dark"] .App-header {
|
|||||||
|
|
||||||
html[data-bs-theme="dark"] .App-link {
|
html[data-bs-theme="dark"] .App-link {
|
||||||
color: #bb86fc;
|
color: #bb86fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-right-chat-button-wrapper {
|
||||||
|
position: fixed; /* 'fixed' faz ele flutuar mesmo com scroll */
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
z-index: 1001; /* Garante que fique acima de outros elementos */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* App.css */
|
||||||
|
|
||||||
|
/* Container principal */
|
||||||
|
.app-wrapper {
|
||||||
|
display: flex; /* Organiza o conteúdo principal e o chat lado a lado */
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
position: relative; /* Essencial para ser o 'pai' */
|
||||||
|
flex-grow: 1;
|
||||||
|
transition: margin-right 0.4s ease-in-out;
|
||||||
|
}
|
||||||
|
.main-content.chat-open {
|
||||||
|
margin-right: 350px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Posicionamento do botão que abre o chat */
|
||||||
|
.chat-button-container {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 20px;
|
||||||
|
right: 20px;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.floating-buttons-container {
|
||||||
|
|
||||||
|
position: fixed;
|
||||||
|
bottom: 20px;
|
||||||
|
right: 20px;
|
||||||
|
z-index: 1000;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
transition: right 0.4s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-buttons-container.chat-open {
|
||||||
|
right: 370px;
|
||||||
}
|
}
|
||||||
56
src/App.js
56
src/App.js
@ -1,40 +1,58 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
|
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
|
||||||
|
import './App.css';
|
||||||
|
|
||||||
// Suas páginas
|
// Suas páginas
|
||||||
|
import LandingPage from './pages/LandingPage';
|
||||||
import Login from "./pages/Login";
|
import Login from "./pages/Login";
|
||||||
import Register from "./pages/Register";
|
import Register from "./pages/Register";
|
||||||
import Forgot from "./pages/ForgotPassword";
|
import Forgot from "./pages/ForgotPassword";
|
||||||
import PerfilSecretaria from "./perfis/perfil_secretaria/PerfilSecretaria";
|
import PerfilSecretaria from "./perfis/perfil_secretaria/PerfilSecretaria";
|
||||||
import LandingPage from './pages/LandingPage';
|
|
||||||
import PerfilFinanceiro from "./perfis/perfil_financeiro/PerfilFinanceiro";
|
import PerfilFinanceiro from "./perfis/perfil_financeiro/PerfilFinanceiro";
|
||||||
import Perfiladm from "./perfis/Perfil_adm/Perfiladm";
|
import Perfiladm from "./perfis/Perfil_adm/Perfiladm";
|
||||||
import PerfilMedico from "./perfis/Perfil_medico/PerfilMedico";
|
import PerfilMedico from "./perfis/Perfil_medico/PerfilMedico";
|
||||||
|
|
||||||
// Componentes globais de acessibilidade
|
// Componentes globais
|
||||||
import VlibrasWidget from "./components/VlibrasWidget";
|
import VlibrasWidget from "./components/VlibrasWidget";
|
||||||
|
import BotaoAcessibilidade from "./components/botaoacessibilidade.jsx";
|
||||||
import BotaoAcessibilidade from "./components/botaoacessibilidade.jsx";
|
import ChatToggleButton from './components/ChatButton/ChatButton';
|
||||||
|
import ChatSidebar from './components/ChatSidebar/ChatSidebar';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
const [isChatOpen, setIsChatOpen] = useState(false);
|
||||||
|
|
||||||
|
const toggleChat = () => {
|
||||||
|
setIsChatOpen(!isChatOpen);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Router>
|
<Router>
|
||||||
<VlibrasWidget />
|
<div className="app-wrapper">
|
||||||
<BotaoAcessibilidade />
|
<div className={`main-content ${isChatOpen ? 'chat-open' : ''}`}>
|
||||||
|
<VlibrasWidget />
|
||||||
|
|
||||||
<Routes>
|
<div className={`floating-buttons-container ${isChatOpen ? 'chat-open' : ''}`}>
|
||||||
<Route path="/" element={<LandingPage />} />
|
<BotaoAcessibilidade />
|
||||||
<Route path="/login" element={<Login />} />
|
<ChatToggleButton onClick={toggleChat} />
|
||||||
<Route path="/register" element={<Register />} />
|
</div>
|
||||||
<Route path="/forgotPassword" element={<Forgot />} />
|
|
||||||
<Route path="/secretaria/*" element={<PerfilSecretaria />} />
|
<Routes>
|
||||||
<Route path="/financeiro/*" element={<PerfilFinanceiro />} />
|
<Route path="/" element={<LandingPage />} />
|
||||||
<Route path="/medico/*" element={<PerfilMedico />} />
|
<Route path="/login" element={<Login />} />
|
||||||
<Route path="/admin/*" element={<Perfiladm />} />
|
<Route path="/register" element={<Register />} />
|
||||||
<Route path="*" element={<h2>Página não encontrada</h2>} />
|
<Route path="/forgotPassword" element={<Forgot />} />
|
||||||
</Routes>
|
<Route path="/secretaria/*" element={<PerfilSecretaria />} />
|
||||||
|
<Route path="/financeiro/*" element={<PerfilFinanceiro />} />
|
||||||
|
<Route path="/medico/*" element={<PerfilMedico />} />
|
||||||
|
<Route path="/admin/*" element={<Perfiladm />} />
|
||||||
|
<Route path="*" element={<h2>Página não encontrada</h2>} />
|
||||||
|
</Routes>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ChatSidebar isOpen={isChatOpen} onClose={toggleChat} />
|
||||||
|
</div>
|
||||||
</Router>
|
</Router>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|
||||||
27
src/components/ChatButton/ChatButton.css
Normal file
27
src/components/ChatButton/ChatButton.css
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
.chat-toggle-button {
|
||||||
|
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;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-toggle-button:hover {
|
||||||
|
background-color: #0056b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-toggle-button svg {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
fill: white;
|
||||||
|
}
|
||||||
15
src/components/ChatButton/ChatButton.jsx
Normal file
15
src/components/ChatButton/ChatButton.jsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import './ChatButton.css';
|
||||||
|
|
||||||
|
const ChatButton = ({ onClick }) => {
|
||||||
|
return (
|
||||||
|
<button className="chat-toggle-button" onClick={onClick} aria-label="Abrir chat">
|
||||||
|
{/* Ícone de Chat (SVG) */}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2z"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ChatButton;
|
||||||
119
src/components/ChatSidebar/ChatSidebar.css
Normal file
119
src/components/ChatSidebar/ChatSidebar.css
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
|
||||||
|
.chat-sidebar {
|
||||||
|
height: 100vh;
|
||||||
|
width: 350px;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: #FFFFFF; /* Fundo branco limpo, como os cards */
|
||||||
|
color: #333; /* Texto escuro padrão */
|
||||||
|
font-family: Arial, sans-serif; /* Use a mesma fonte do seu site, se souber */
|
||||||
|
transform: translateX(100%);
|
||||||
|
transition: transform 0.4s ease-in-out;
|
||||||
|
box-shadow: -2px 0 15px rgba(0, 0, 0, 0.1);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-sidebar.open {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
.chat-header {
|
||||||
|
padding: 15px 20px;
|
||||||
|
background-color: #F8F9FA; /* Cinza bem claro, como o fundo do site */
|
||||||
|
border-bottom: 1px solid #E9ECEF; /* Borda sutil */
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #212529; /* Cor do título dos cards */
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-close-button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
font-size: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #6c757d; /* Cinza sutil para o ícone de fechar */
|
||||||
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
|
.chat-close-button:hover {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Área das Mensagens */
|
||||||
|
.chat-messages {
|
||||||
|
flex-grow: 1;
|
||||||
|
padding: 20px;
|
||||||
|
overflow-y: auto;
|
||||||
|
background-color: #F8F9FA; /* Fundo igual ao header para consistência */
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px; /* Espaçamento entre as mensagens */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adicionando estilo para os balões de mensagem */
|
||||||
|
.message {
|
||||||
|
padding: 10px 15px;
|
||||||
|
border-radius: 18px;
|
||||||
|
max-width: 80%;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.received {
|
||||||
|
background-color: #E9ECEF; /* Cinza claro para mensagens recebidas */
|
||||||
|
color: #212529;
|
||||||
|
align-self: flex-start; /* Alinha à esquerda */
|
||||||
|
border-bottom-left-radius: 4px; /* Detalhe de estilo */
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.sent {
|
||||||
|
background-color: #6C63FF; /* Cor principal do seu site */
|
||||||
|
color: white;
|
||||||
|
align-self: flex-end; /* Alinha à direita */
|
||||||
|
border-bottom-right-radius: 4px; /* Detalhe de estilo */
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-input-area {
|
||||||
|
padding: 15px 20px;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px; /* Espaço entre o input e o botão */
|
||||||
|
border-top: 1px solid #E9ECEF;
|
||||||
|
background-color: #FFFFFF; /* Fundo branco para destacar */
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-input-area input {
|
||||||
|
flex-grow: 1;
|
||||||
|
border: 1px solid #DEE2E6; /* Borda padrão */
|
||||||
|
border-radius: 8px; /* Bordas arredondadas como os elementos do site */
|
||||||
|
padding: 12px 15px;
|
||||||
|
font-size: 15px;
|
||||||
|
color: #333;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-input-area input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #6C63FF; /* Destaque com a cor principal */
|
||||||
|
box-shadow: 0 0 0 2px rgba(108, 99, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-input-area button {
|
||||||
|
background-color: #6C63FF; /* Cor principal do seu site */
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
padding: 0 25px;
|
||||||
|
border-radius: 8px; /* Mesma borda do input */
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
.chat-input-area button:hover {
|
||||||
|
background-color: #574ee6; /* Um tom um pouco mais escuro ao passar o mouse */
|
||||||
|
}
|
||||||
79
src/components/ChatSidebar/ChatSidebar.jsx
Normal file
79
src/components/ChatSidebar/ChatSidebar.jsx
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
|
import './ChatSidebar.css';
|
||||||
|
|
||||||
|
const ChatSidebar = ({ isOpen, onClose }) => {
|
||||||
|
|
||||||
|
const [messages, setMessages] = useState([
|
||||||
|
{ id: 1, text: 'Olá! Como podemos ajudar você hoje?', sender: 'support' }
|
||||||
|
]);
|
||||||
|
const [inputValue, setInputValue] = useState('');
|
||||||
|
const messagesEndRef = useRef(null);
|
||||||
|
const sidebarClassName = `chat-sidebar ${isOpen ? 'open' : ''}`;
|
||||||
|
const scrollToBottom = () => {
|
||||||
|
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
scrollToBottom();
|
||||||
|
}, [messages]);
|
||||||
|
|
||||||
|
const handleSendMessage = () => {
|
||||||
|
if (inputValue.trim() === '') return;
|
||||||
|
const newMessage = {
|
||||||
|
id: messages.length + 1,
|
||||||
|
text: inputValue,
|
||||||
|
sender: 'user'
|
||||||
|
};
|
||||||
|
setMessages(currentMessages => [...currentMessages, newMessage]);
|
||||||
|
setInputValue('');
|
||||||
|
setTimeout(() => {
|
||||||
|
const supportReply = {
|
||||||
|
id: messages.length + 2,
|
||||||
|
text: 'Obrigado por sua mensagem. Um de nossos atendentes responderá em breve.',
|
||||||
|
sender: 'support'
|
||||||
|
};
|
||||||
|
setMessages(currentMessages => [...currentMessages, supportReply]);
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
const handleKeyPress = (event) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
handleSendMessage();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={sidebarClassName}>
|
||||||
|
<div className="chat-header">
|
||||||
|
<h3>Suporte Online</h3>
|
||||||
|
<button onClick={onClose} className="chat-close-button">X</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="chat-messages">
|
||||||
|
{/* Mapeia a lista de mensagens e cria um balão para cada uma */}
|
||||||
|
{messages.map(message => (
|
||||||
|
<div
|
||||||
|
key={message.id}
|
||||||
|
className={`message ${message.sender === 'user' ? 'sent' : 'received'}`}
|
||||||
|
>
|
||||||
|
{message.text}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{/* Elemento invisível no final para o scroll automático */}
|
||||||
|
<div ref={messagesEndRef} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="chat-input-area">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Digite sua mensagem..."
|
||||||
|
value={inputValue}
|
||||||
|
onChange={(e) => setInputValue(e.target.value)}
|
||||||
|
onKeyPress={handleKeyPress}
|
||||||
|
/>
|
||||||
|
<button onClick={handleSendMessage}>Enviar</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ChatSidebar;
|
||||||
@ -46,7 +46,7 @@ const TrocardePerfis = () => {
|
|||||||
value={selectedProfile}
|
value={selectedProfile}
|
||||||
onChange={handleSelectChange}
|
onChange={handleSelectChange}
|
||||||
>
|
>
|
||||||
<option value="">Selecionar perfil</option>
|
<option value="" disabled invisible>Selecionar perfil</option>
|
||||||
{options.map((opt) => (
|
{options.map((opt) => (
|
||||||
<option key={opt.key} value={opt.route}>
|
<option key={opt.key} value={opt.route}>
|
||||||
{opt.label}
|
{opt.label}
|
||||||
|
|||||||
@ -1,272 +1,198 @@
|
|||||||
/* --- ESTILO PARA ESCONDER O BOTÃO ORIGINAL DO VLIBRAS --- */
|
@import url('https://fonts.cdnfonts.com/css/open-dyslexic');
|
||||||
|
|
||||||
[vw-access-button] {
|
[vw-access-button] {
|
||||||
display: none !important;
|
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 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.botao-flutuante-acessibilidade {
|
.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;
|
width: 60px;
|
||||||
height: 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;
|
cursor: pointer;
|
||||||
box-shadow: 0 5px 15px rgba(0, 91, 179, 0.4);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease;
|
justify-content: center;
|
||||||
margin-top: 15px; /* Distância do menu */
|
|
||||||
pointer-events: auto; /* Permite que o botão seja clicável */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.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 {
|
.menu-opcoes {
|
||||||
background-color: #ffffff;
|
position: absolute;
|
||||||
border-radius: 12px;
|
bottom: 70px;
|
||||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
|
right: 0;
|
||||||
padding: 8px;
|
|
||||||
width: 280px;
|
width: 280px;
|
||||||
z-index: 1; /* Abaixo do botão principal */
|
max-height: calc(100vh - 100px);
|
||||||
border: 1px solid #e9ecef;
|
overflow-y: auto;
|
||||||
|
background-color: white;
|
||||||
/* Animação */
|
border-radius: 8px;
|
||||||
transform-origin: bottom center;
|
box-shadow: 0 6px 12px rgba(0,0,0,0.3);
|
||||||
transform: translateY(10px) scale(0.95);
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 15px;
|
||||||
|
transform: scale(0.95);
|
||||||
|
transform-origin: bottom right;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
transition: all 0.2s ease;
|
transition: transform 0.2s ease, opacity 0.2s ease, visibility 0.2s;
|
||||||
pointer-events: auto; /* Permite que o menu seja clicável */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-opcoes.aberto {
|
.menu-opcoes.aberto {
|
||||||
transform: translateY(0) scale(1);
|
transform: scale(1);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-titulo {
|
.menu-titulo {
|
||||||
font-size: 14px;
|
font-size: 18px;
|
||||||
font-weight: 600;
|
font-weight: bold;
|
||||||
color: #6c757d;
|
margin-bottom: 10px;
|
||||||
padding: 8px 12px;
|
text-align: center;
|
||||||
border-bottom: 1px solid #f1f3f5;
|
color: #333;
|
||||||
margin-bottom: 5px;
|
|
||||||
transition: color 0.2s ease, border-bottom-color 0.2s ease;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- ESTILOS DOS BOTÕES E DA CHECKBOX NO MENU --- */
|
|
||||||
.menu-opcoes button,
|
.menu-opcoes button,
|
||||||
.checkbox-label-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;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: 10px;
|
||||||
background-color: transparent;
|
font-size: 15px;
|
||||||
border: none;
|
color: #333;
|
||||||
padding: 12px;
|
transition: background-color 0.2s;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-opcoes button:hover,
|
.menu-opcoes button:hover,
|
||||||
.checkbox-label-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"] {
|
.checkbox-label-button input[type="checkbox"] {
|
||||||
appearance: none;
|
margin-left: auto;
|
||||||
-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;
|
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
background-color: white;
|
cursor: pointer;
|
||||||
border-radius: 50%;
|
|
||||||
box-shadow: 0 1px 3px rgba(0,0,0,0.2);
|
|
||||||
transition: transform 0.3s ease-in-out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox-label-button input[type="checkbox"]:checked {
|
/* Tamanho da Fonte */
|
||||||
background-color: #0d6efd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-label-button input[type="checkbox"]:checked::before {
|
|
||||||
transform: translateX(20px);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* --- ✨ NOVOS ESTILOS PARA O CONTROLE DE FONTE ✨ --- */
|
|
||||||
|
|
||||||
.font-size-control {
|
.font-size-control {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
padding: 10px;
|
||||||
padding: 8px 12px;
|
background-color: rgba(0, 0, 0, 0.05);
|
||||||
color: #212529;
|
border-radius: 8px;
|
||||||
transition: color 0.2s ease;
|
margin-bottom: 5px;
|
||||||
border-top: 1px solid #f1f3f5;
|
gap: 10px;
|
||||||
margin-top: 5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.font-size-label {
|
.font-size-label {
|
||||||
font-size: 16px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.font-size-buttons {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
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;
|
font-weight: 600;
|
||||||
color: #495057;
|
font-size: 15px;
|
||||||
min-width: 45px;
|
}
|
||||||
|
.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;
|
text-align: center;
|
||||||
transition: color 0.2s ease;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dark mode styles */
|
/* Fonte para Dislexia */
|
||||||
html[data-bs-theme="dark"] {
|
.dyslexia-font-active * {
|
||||||
|
font-family: 'Open-Dyslexic', sans-serif !important;
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="black" stroke="white" stroke-width="2" d="M13.64,21.97C13.14,22.21 12.54,22 12.31,21.5L10.13,16.76L7.62,18.78C7.45,18.92 7.24,19 7,19A1,1 0 0,1 6,18V4A1,1 0 0,1 7,3C7.24,3 7.45,3.08 7.62,3.22L16.39,10.22C16.78,10.5 16.88,11.05 16.6,11.45L13.64,21.97Z" /></svg>') 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; }
|
||||||
@ -2,41 +2,131 @@ import React, { useState, useEffect, useRef } from 'react';
|
|||||||
import './botaoacessibilidade.css'; // Importando o CSS
|
import './botaoacessibilidade.css'; // Importando o CSS
|
||||||
import { setTheme } from '../assets/static/js/components/dark';
|
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 (
|
||||||
|
<>
|
||||||
|
<div ref={topMaskRef} className="reading-guide-mask reading-guide-top"></div>
|
||||||
|
<div ref={bottomMaskRef} className="reading-guide-mask reading-guide-bottom"></div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- COMPONENTE PRINCIPAL ---
|
||||||
function BotaoAcessibilidade() {
|
function BotaoAcessibilidade() {
|
||||||
|
// Estados de todas as funcionalidades
|
||||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||||
const [isReadOnHoverActive, setIsReadOnHoverActive] = useState(false);
|
|
||||||
const [isDarkMode, setIsDarkMode] = 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);
|
const lastSpokenTargetRef = useRef(null);
|
||||||
|
|
||||||
|
// Efeitos para aplicar as funcionalidades na página
|
||||||
|
useEffect(() => { setTheme(isDarkMode ? "dark" : "light", true); }, [isDarkMode]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTheme(isDarkMode ? "dark" : "light", true);
|
const originalFontSize = document.documentElement.style.fontSize;
|
||||||
}, [isDarkMode]);
|
document.documentElement.style.fontSize = `${fontSize * 100}%`;
|
||||||
|
return () => { document.documentElement.style.fontSize = originalFontSize; };
|
||||||
|
}, [fontSize]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isReadOnHoverActive) {
|
document.body.classList.toggle('big-cursor-active', isBigCursor);
|
||||||
window.speechSynthesis.cancel();
|
return () => { document.body.classList.remove('big-cursor-active'); };
|
||||||
return;
|
}, [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) => {
|
}, [colorblindMode]);
|
||||||
const target = event.target;
|
// VERSÃO DEFINITIVA - Muito mais abrangente
|
||||||
if (target && target !== lastSpokenTargetRef.current && target.innerText) {
|
useEffect(() => {
|
||||||
const text = target.innerText.trim();
|
if (!isReadOnHoverActive) {
|
||||||
if (text.length > 0 && ['P', 'H1', 'H2', 'H3', 'BUTTON', 'A', 'LI', 'LABEL'].includes(target.tagName)) {
|
window.speechSynthesis.cancel();
|
||||||
lastSpokenTargetRef.current = target;
|
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();
|
window.speechSynthesis.cancel();
|
||||||
const utterance = new SpeechSynthesisUtterance(text);
|
const utterance = new SpeechSynthesisUtterance(textToSpeak);
|
||||||
utterance.lang = 'pt-BR';
|
utterance.lang = 'pt-BR';
|
||||||
window.speechSynthesis.speak(utterance);
|
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 handleVlibrasClick = () => {
|
||||||
const originalVlibrasButton = document.querySelector('[vw-access-button]');
|
const originalVlibrasButton = document.querySelector('[vw-access-button]');
|
||||||
if (originalVlibrasButton) {
|
if (originalVlibrasButton) {
|
||||||
@ -47,53 +137,96 @@ function BotaoAcessibilidade() {
|
|||||||
setIsMenuOpen(false);
|
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 (
|
return (
|
||||||
<div className={`container-acessibilidade ${isDarkMode ? 'dark-mode' : ''}`}>
|
<div className={`container-acessibilidade ${isDarkMode ? 'dark-mode' : ''}`}>
|
||||||
|
{isReadingGuide && <GuiaDeLeitura />}
|
||||||
|
<svg style={{ position: 'absolute', height: 0, width: 0 }}>
|
||||||
|
<defs>
|
||||||
|
<filter id="protanopia"><feColorMatrix in="SourceGraphic" type="matrix" values="0.567, 0.433, 0, 0, 0, 0.558, 0.442, 0, 0, 0, 0, 0.242, 0.758, 0, 0, 0, 0, 0, 1, 0"/></filter>
|
||||||
|
<filter id="deuteranopia"><feColorMatrix in="SourceGraphic" type="matrix" values="0.625, 0.375, 0, 0, 0, 0.7, 0.3, 0, 0, 0, 0, 0.3, 0.7, 0, 0, 0, 0, 0, 1, 0"/></filter>
|
||||||
|
<filter id="tritanopia"><feColorMatrix in="SourceGraphic" type="matrix" values="0.95, 0.05, 0, 0, 0, 0, 0.433, 0.567, 0, 0, 0, 0.475, 0.525, 0, 0, 0, 0, 0, 1, 0"/></filter>
|
||||||
|
<filter id="achromatopsia"><feColorMatrix in="SourceGraphic" type="matrix" values="0.299, 0.587, 0.114, 0, 0, 0.299, 0.587, 0.114, 0, 0, 0.299, 0.587, 0.114, 0, 0, 0, 0, 0, 1, 0"/></filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
<div className={`menu-opcoes ${isMenuOpen ? 'aberto' : ''}`}>
|
<div className={`menu-opcoes ${isMenuOpen ? 'aberto' : ''}`}>
|
||||||
<div className="menu-titulo">Acessibilidade</div>
|
<div className="menu-titulo">Acessibilidade</div>
|
||||||
|
|
||||||
|
<div className="font-size-control">
|
||||||
|
<div className="font-size-label">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="4 7 4 4 20 4 20 7"></polyline><line x1="9" y1="20" x2="15" y2="20"></line><line x1="12" y1="4" x2="12" y2="20"></line></svg>
|
||||||
|
Tamanho da Fonte
|
||||||
|
</div>
|
||||||
|
<div className="font-size-buttons">
|
||||||
|
<button onClick={handleDecreaseFontSize} title="Diminuir Fonte">A-</button>
|
||||||
|
<span>{`${Math.round(fontSize * 100)}%`}</span>
|
||||||
|
<button onClick={handleIncreaseFontSize} title="Aumentar Fonte">A+</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="acessibilidade-select-control">
|
||||||
|
<label htmlFor="colorblind-select">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path></svg>
|
||||||
|
Modo Daltonismo
|
||||||
|
</label>
|
||||||
|
<select id="colorblind-select" value={colorblindMode} onChange={(e) => setColorblindMode(e.target.value)}>
|
||||||
|
<option value="none">Desativado</option>
|
||||||
|
<option value="protanopia">Protanopia</option>
|
||||||
|
<option value="deuteranopia">Deuteranopia</option>
|
||||||
|
<option value="tritanopia">Tritanopia</option>
|
||||||
|
<option value="achromatopsia">Monocromático</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<label htmlFor="darkModeCheckbox" className="checkbox-label-button">
|
<label htmlFor="darkModeCheckbox" className="checkbox-label-button">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>
|
||||||
Modo Escuro
|
Modo Escuro
|
||||||
<input
|
<input type="checkbox" id="darkModeCheckbox" checked={isDarkMode} onChange={() => setIsDarkMode(!isDarkMode)} />
|
||||||
type="checkbox"
|
|
||||||
id="darkModeCheckbox"
|
|
||||||
checked={isDarkMode}
|
|
||||||
onChange={() => setIsDarkMode(!isDarkMode)}
|
|
||||||
/>
|
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<label htmlFor="dyslexiaFontCheckbox" className="checkbox-label-button">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>
|
||||||
|
Fonte para Dislexia
|
||||||
|
<input type="checkbox" id="dyslexiaFontCheckbox" checked={isDyslexiaFont} onChange={() => setIsDyslexiaFont(!isDyslexiaFont)} />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label htmlFor="letterSpacingCheckbox" className="checkbox-label-button">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path></svg>
|
||||||
|
Espaçamento entre Letras
|
||||||
|
<input type="checkbox" id="letterSpacingCheckbox" checked={isLetterSpacing} onChange={() => setIsLetterSpacing(!isLetterSpacing)} />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label htmlFor="lineHeightCheckbox" className="checkbox-label-button">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg>
|
||||||
|
Altura da Linha
|
||||||
|
<input type="checkbox" id="lineHeightCheckbox" checked={isLineHeight} onChange={() => setIsLineHeight(!isLineHeight)} />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label htmlFor="readingGuideCheckbox" className="checkbox-label-button">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="8" y1="6" x2="21" y2="6"></line><line x1="8" y1="12" x2="21" y2="12"></line><line x1="8" y1="18" x2="21" y2="18"></line><line x1="3" y1="6" x2="3.01" y2="6"></line><line x1="3" y1="12" x2="3.01" y2="12"></line><line x1="3" y1="18" x2="3.01" y2="18"></line></svg>
|
||||||
|
Guia de Leitura
|
||||||
|
<input type="checkbox" id="readingGuideCheckbox" checked={isReadingGuide} onChange={() => setIsReadingGuide(!isReadingGuide)} />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label htmlFor="bigCursorCheckbox" className="checkbox-label-button">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 3l7.07 16.97 2.51-7.39 7.39-2.51L3 3z"></path><path d="M13 13l6 6"></path></svg>
|
||||||
|
Cursor Grande
|
||||||
|
<input type="checkbox" id="bigCursorCheckbox" checked={isBigCursor} onChange={() => setIsBigCursor(!isBigCursor)} />
|
||||||
|
</label>
|
||||||
|
|
||||||
<label htmlFor="readOnHoverCheckbox" className="checkbox-label-button">
|
<label htmlFor="readOnHoverCheckbox" className="checkbox-label-button">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 3l7.07 16.97 2.51-7.39 7.39-2.51L3 3z"></path><path d="M13 13l6 6"></path></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 3l7.07 16.97 2.51-7.39 7.39-2.51L3 3z"></path><path d="M13 13l6 6"></path></svg>
|
||||||
Leitura instantânea
|
Leitura instantânea
|
||||||
<input
|
<input type="checkbox" id="readOnHoverCheckbox" checked={isReadOnHoverActive} onChange={() => setIsReadOnHoverActive(!isReadOnHoverActive)} />
|
||||||
type="checkbox"
|
|
||||||
id="readOnHoverCheckbox"
|
|
||||||
checked={isReadOnHoverActive}
|
|
||||||
onChange={() => setIsReadOnHoverActive(!isReadOnHoverActive)}
|
|
||||||
/>
|
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
{/* ADICIONADO DE VOLTA: Botão para LIBRAS */}
|
||||||
<button onClick={handleVlibrasClick}>
|
<button onClick={handleVlibrasClick}>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M18 11V6a2 2 0 0 0-2-2v0a2 2 0 0 0-2 2v0" /><path d="M14 10V4a2 2 0 0 0-2-2v0a2 2 0 0 0-2 2v2" /><path d="M10 10.5V6a2 2 0 0 0-2-2v0a2 2 0 0 0-2 2v8" /><path d="M18 8a2 2 0 1 1 4 0v6a8 8 0 0 1-8 8h-4a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2h2.3" /></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M18 11V6a2 2 0 0 0-2-2v0a2 2 0 0 0-2 2v0" /><path d="M14 10V4a2 2 0 0 0-2-2v0a2 2 0 0 0-2 2v2" /><path d="M10 10.5V6a2 2 0 0 0-2-2v0a2 2 0 0 0-2 2v8" /><path d="M18 8a2 2 0 1 1 4 0v6a8 8 0 0 1-8 8h-4a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2h2.3" /></svg>
|
||||||
Traduzir para LIBRAS
|
Traduzir para LIBRAS
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button
|
|
||||||
className="botao-flutuante-acessibilidade"
|
<button className="botao-flutuante-acessibilidade" onClick={() => setIsMenuOpen(!isMenuOpen)} title="Menu de Acessibilidade">
|
||||||
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
|
||||||
title="Menu de Acessibilidade"
|
|
||||||
>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="30" height="30" fill="white">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="30" height="30" fill="white">
|
||||||
<path d="M12 2c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm9 7h-6v13h-2v-6h-2v6H9V9H3V7h18v2z" />
|
<path d="M12 2c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm9 7h-6v13h-2v-6h-2v6H9V9H3V7h18v2z" />
|
||||||
</svg>
|
</svg>
|
||||||
@ -101,6 +234,4 @@ function BotaoAcessibilidade() {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
export default BotaoAcessibilidade;
|
||||||
export default BotaoAcessibilidade;
|
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user