UNPKG

@mui/x-charts

Version:

The community edition of MUI X Charts components.

96 lines (89 loc) 3.6 kB
'use client'; import * as React from 'react'; import { useChartId } from "../../../hooks/useChartId.mjs"; import { useDescription } from "./useDescription.mjs"; /** * Make the proxy looks like a layer. * Having a non-zero size is important for some screen readers to announce the content. */ import { jsx as _jsx } from "react/jsx-runtime"; const fullSizeLayerStyle = { borderWidth: 0, width: '100%', height: '100%', overflow: 'hidden', position: 'absolute', inset: 0, padding: 0, outline: 'none', pointerEvents: 'none' }; // The proxy is implemented by having two divs with the same content, and toggling the visibility of each one when the content changes. // The idea is to imitate the behavior of the focus moving from a list element to another, but with the minimal number of DOM elements. /** * This component provides an accessibility proxy for charts. * It uses two divs to let screen readers announce the focused content when it changes. */ export function ChartsAccessibilityProxy() { const message = useDescription(); const chartId = useChartId(); const currentFormatRef = React.useRef(null); const currentIndexRef = React.useRef(0); const containerRef = React.useRef(null); React.useEffect(() => { const container = containerRef.current; if (!container) { return; } // Initialize children if not present if (container.children.length === 0) { for (let i = 0; i < 2; i += 1) { const div = document.createElement('div'); div.setAttribute('id', i === 0 ? `voiceover-${chartId}-1` : `voiceover-${chartId}-2`); div.style.display = 'none'; container.appendChild(div); } // The divs with the message content for (let i = 0; i < 2; i += 1) { const div = document.createElement('div'); if (i === (currentIndexRef.current + 1) % 2 && message) { div.setAttribute('tabindex', '0'); } div.setAttribute('role', 'img'); div.setAttribute('aria-labelledby', i === 0 ? `voiceover-${chartId}-1` : `voiceover-${chartId}-2`); div.style.width = '100%'; div.style.height = '100%'; div.style.outline = 'none'; container.appendChild(div); } } if (message && message !== currentFormatRef.current) { currentFormatRef.current = message; const inactiveIndex = currentIndexRef.current; currentIndexRef.current = (currentIndexRef.current + 1) % 2; const activeIndex = currentIndexRef.current; const activeDiv = container.children[2 + activeIndex]; const inactiveDiv = container.children[2 + inactiveIndex]; const activeTextDiv = container.children[activeIndex]; const inactiveTextDiv = container.children[inactiveIndex]; // Both get text update activeTextDiv.textContent = message ?? ''; inactiveTextDiv.textContent = message ?? ''; activeDiv.setAttribute('aria-hidden', 'false'); activeDiv.setAttribute('aria-labelledby', activeIndex === 0 ? `voiceover-${chartId}-1` : `voiceover-${chartId}-2`); if (message) { activeDiv.setAttribute('tabindex', '0'); } inactiveDiv.setAttribute('aria-hidden', 'true'); inactiveDiv.setAttribute('aria-labelledby', activeIndex === 0 ? `voiceover-${chartId}-1` : `voiceover-${chartId}-2`); inactiveDiv.removeAttribute('tabindex'); activeDiv.focus(); } }, [message, chartId]); return /*#__PURE__*/_jsx("div", { role: "presentation", tabIndex: message ? undefined : 0, ref: containerRef, style: fullSizeLayerStyle }); }