UNPKG

vlibras-player-webjs

Version:

Biblioteca JavaScript moderna para integração do VLibras Player com React, Vue, Angular e vanilla JS

223 lines 8.74 kB
import { useState, useEffect, useCallback, useRef } from 'react'; import { isBrowser, safeWindow, safeDocument } from '../ssr/useIsomorphicLayoutEffect'; import { useVLibras } from './useVLibras'; /** * Hook para funcionalidades de acessibilidade do VLibras */ export function useVLibrasAccessibility(initialOptions = {}) { const defaultOptions = { enableScreenReader: true, announceTranslations: true, enableKeyboardNav: true, enableReducedMotion: false, enableHighContrast: false, textSize: 'medium', language: 'pt-BR' }; const { player, isLoaded, isPlaying } = useVLibras(); const [options, setOptions] = useState({ ...defaultOptions, ...initialOptions }); const announceRef = useRef(null); // Detectar preferências do sistema const [isReducedMotion, setIsReducedMotion] = useState(false); const [isHighContrast, setIsHighContrast] = useState(false); // Detectar preferências de movimento reduzido useEffect(() => { if (!isBrowser || !safeWindow?.matchMedia) return; const mediaQuery = safeWindow.matchMedia('(prefers-reduced-motion: reduce)'); setIsReducedMotion(mediaQuery.matches); const handleChange = (e) => { setIsReducedMotion(e.matches); setOptions(prev => ({ ...prev, enableReducedMotion: e.matches })); }; mediaQuery.addEventListener('change', handleChange); return () => mediaQuery.removeEventListener('change', handleChange); }, []); // Detectar preferências de alto contraste useEffect(() => { if (!isBrowser || !safeWindow?.matchMedia) return; const mediaQuery = safeWindow.matchMedia('(prefers-contrast: high)'); setIsHighContrast(mediaQuery.matches); const handleChange = (e) => { setIsHighContrast(e.matches); setOptions(prev => ({ ...prev, enableHighContrast: e.matches })); }; mediaQuery.addEventListener('change', handleChange); return () => mediaQuery.removeEventListener('change', handleChange); }, []); // Criar região de anúncios para leitores de tela useEffect(() => { if (!options.enableScreenReader || !isBrowser || !safeDocument) return; let announceDiv = safeDocument.getElementById('vlibras-announcements'); if (!announceDiv) { announceDiv = safeDocument.createElement('div'); announceDiv.id = 'vlibras-announcements'; announceDiv.setAttribute('aria-live', 'polite'); announceDiv.setAttribute('aria-atomic', 'true'); announceDiv.style.position = 'absolute'; announceDiv.style.left = '-10000px'; announceDiv.style.width = '1px'; announceDiv.style.height = '1px'; announceDiv.style.overflow = 'hidden'; safeDocument.body.appendChild(announceDiv); } announceRef.current = announceDiv; return () => { if (announceDiv && announceDiv.parentNode) { announceDiv.parentNode.removeChild(announceDiv); } }; }, [options.enableScreenReader]); const updateOptions = useCallback((newOptions) => { setOptions(prev => ({ ...prev, ...newOptions })); }, []); const announce = useCallback((message, priority = 'polite') => { if (!options.enableScreenReader || !announceRef.current) return; announceRef.current.setAttribute('aria-live', priority); announceRef.current.textContent = message; // Limpar depois de um tempo para permitir novos anúncios setTimeout(() => { if (announceRef.current) { announceRef.current.textContent = ''; } }, 1000); }, [options.enableScreenReader]); const focusPlayer = useCallback(() => { if (!isBrowser || !safeDocument) return; const playerElement = safeDocument.querySelector('[data-vlibras-player]'); if (playerElement && playerElement.focus) { playerElement.focus(); } }, []); const getKeyboardShortcuts = useCallback(() => { return { 'Space': 'Pausar/Continuar reprodução', 'Enter': 'Traduzir texto selecionado', 'Escape': 'Parar reprodução', 'r': 'Repetir última tradução', 'f': 'Focar no player', '+/-': 'Aumentar/Diminuir velocidade', '1-4': 'Alterar tamanho do player' }; }, []); // Configurar atalhos de teclado useEffect(() => { if (!options.enableKeyboardNav) return; const handleKeyPress = (event) => { // Evitar conflitos quando usuário está digitando if (event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement) { return; } switch (event.key.toLowerCase()) { case ' ': event.preventDefault(); if (isPlaying) { player?.pause(); announce('Reprodução pausada'); } else { player?.play(); announce('Reprodução iniciada'); } break; case 'enter': event.preventDefault(); const selectedText = window.getSelection()?.toString(); if (selectedText && player) { player.translateAsync(selectedText); announce(`Traduzindo: ${selectedText}`); } break; case 'escape': event.preventDefault(); player?.stop(); announce('Reprodução parada'); break; case 'r': if (event.ctrlKey || event.metaKey) return; // Evitar conflito com refresh event.preventDefault(); player?.play(); announce('Repetindo última tradução'); break; case 'f': if (event.ctrlKey || event.metaKey) return; // Evitar conflito com busca event.preventDefault(); focusPlayer(); break; } }; if (isBrowser && safeDocument) { safeDocument.addEventListener('keydown', handleKeyPress); return () => { if (safeDocument) { safeDocument.removeEventListener('keydown', handleKeyPress); } }; } return undefined; }, [options.enableKeyboardNav, isPlaying, player, announce, focusPlayer]); // Anunciar mudanças de estado useEffect(() => { if (!options.announceTranslations) return; if (isLoaded) { announce('VLibras carregado e pronto para uso'); } }, [isLoaded, options.announceTranslations, announce]); useEffect(() => { if (!options.announceTranslations) return; if (isPlaying) { announce('Reproduzindo tradução em Libras'); } }, [isPlaying, options.announceTranslations, announce]); // Aplicar estilos de acessibilidade useEffect(() => { if (!isBrowser || !safeDocument) return; const root = safeDocument.documentElement; // Tamanho do texto const textSizeMap = { 'small': '0.875rem', 'medium': '1rem', 'large': '1.125rem', 'x-large': '1.25rem' }; root.style.setProperty('--vlibras-text-size', textSizeMap[options.textSize || 'medium']); // Alto contraste if (options.enableHighContrast) { root.classList.add('vlibras-high-contrast'); } else { root.classList.remove('vlibras-high-contrast'); } // Movimento reduzido if (options.enableReducedMotion) { root.classList.add('vlibras-reduced-motion'); } else { root.classList.remove('vlibras-reduced-motion'); } }, [options.textSize, options.enableHighContrast, options.enableReducedMotion]); return { options, updateOptions, announce, isReducedMotion, isHighContrast, focusPlayer, getKeyboardShortcuts }; } //# sourceMappingURL=useVLibrasAccessibility.js.map