136 lines
5.1 KiB
JavaScript
136 lines
5.1 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "useDomAnnounce_unstable", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return useDomAnnounce_unstable;
|
|
}
|
|
});
|
|
const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard");
|
|
const _reactsharedcontexts = require("@fluentui/react-shared-contexts");
|
|
const _reactutilities = require("@fluentui/react-utilities");
|
|
const _react = /*#__PURE__*/ _interop_require_wildcard._(require("react"));
|
|
/** The duration the message needs to be in present in DOM for screen readers to register a change and announce */ const MESSAGE_DURATION = 500;
|
|
const VISUALLY_HIDDEN_STYLES = {
|
|
clip: 'rect(0px, 0px, 0px, 0px)',
|
|
height: '1px',
|
|
margin: '-1px',
|
|
width: '1px',
|
|
position: 'absolute',
|
|
overflow: 'hidden',
|
|
textWrap: 'nowrap'
|
|
};
|
|
const useDomAnnounce_unstable = ()=>{
|
|
const { targetDocument } = (0, _reactsharedcontexts.useFluent_unstable)();
|
|
const timeoutRef = _react.useRef(undefined);
|
|
const [setAnnounceTimeout, clearAnnounceTimeout] = (0, _reactutilities.useTimeout)();
|
|
const elementRef = _react.useRef(null);
|
|
const order = _react.useRef(0);
|
|
// investigate alert implementation later
|
|
// const [alertList, setAlertList] = React.useState<string[]>([]);
|
|
const batchMessages = _react.useRef([]);
|
|
const [messageQueue] = _react.useState(()=>(0, _reactutilities.createPriorityQueue)((a, b)=>{
|
|
if (a.priority !== b.priority) {
|
|
return b.priority - a.priority;
|
|
}
|
|
return a.createdAt - b.createdAt;
|
|
}));
|
|
const queueMessage = _react.useCallback(()=>{
|
|
if (timeoutRef.current || !elementRef.current) {
|
|
return;
|
|
}
|
|
const runCycle = ()=>{
|
|
if (!elementRef.current) {
|
|
return;
|
|
}
|
|
if (targetDocument && messageQueue.peek()) {
|
|
// need a wrapping element for Narrator/Edge, which currently does not pick up text-only live region changes
|
|
// consistently
|
|
// if this is fixed, we can set textContent to the string directly
|
|
const wrappingEl = targetDocument.createElement('span');
|
|
wrappingEl.innerText = messageQueue.all().filter((msg)=>msg.message.trim().length > 0).reduce((prevText, currMsg)=>prevText + currMsg.message + '. ', '');
|
|
elementRef.current.innerText = '';
|
|
elementRef.current.appendChild(wrappingEl);
|
|
messageQueue.clear();
|
|
batchMessages.current = [];
|
|
// begin new cycle to clear (or update) messages
|
|
timeoutRef.current = setAnnounceTimeout(()=>{
|
|
runCycle();
|
|
}, MESSAGE_DURATION);
|
|
} else {
|
|
elementRef.current.textContent = '';
|
|
clearAnnounceTimeout();
|
|
timeoutRef.current = undefined;
|
|
}
|
|
};
|
|
runCycle();
|
|
}, [
|
|
clearAnnounceTimeout,
|
|
messageQueue,
|
|
setAnnounceTimeout,
|
|
targetDocument
|
|
]);
|
|
const announce = _react.useCallback((message, options = {})=>{
|
|
const { alert = false, priority = 0, batchId } = options;
|
|
// check if message is an alert
|
|
if (alert) {
|
|
// TODO: alert implementation
|
|
// setAlertList([...alertList, message]);
|
|
}
|
|
const liveMessage = {
|
|
message,
|
|
createdAt: order.current++,
|
|
priority,
|
|
batchId
|
|
};
|
|
// check if batchId exists
|
|
if (batchId) {
|
|
// update associated msg if it does
|
|
const batchMessage = batchMessages.current.find((msg)=>msg.batchId === batchId);
|
|
if (batchMessage) {
|
|
// replace existing message in queue
|
|
messageQueue.remove(batchMessage.message);
|
|
// update list of existing batchIds w/ most recent message
|
|
batchMessage.message = liveMessage;
|
|
} else {
|
|
// update list of existing batchIds, add new if doesn't already exist
|
|
batchMessages.current = [
|
|
...batchMessages.current,
|
|
{
|
|
batchId,
|
|
message: liveMessage
|
|
}
|
|
];
|
|
}
|
|
}
|
|
// add new message
|
|
messageQueue.enqueue(liveMessage);
|
|
queueMessage();
|
|
}, [
|
|
messageQueue,
|
|
queueMessage
|
|
]);
|
|
_react.useEffect(()=>{
|
|
if (!targetDocument) {
|
|
return;
|
|
}
|
|
const element = targetDocument.createElement('div');
|
|
element.setAttribute('aria-live', 'assertive');
|
|
Object.assign(element.style, VISUALLY_HIDDEN_STYLES);
|
|
targetDocument.body.append(element);
|
|
elementRef.current = element;
|
|
return ()=>{
|
|
element.remove();
|
|
elementRef.current = null;
|
|
clearAnnounceTimeout();
|
|
timeoutRef.current = undefined;
|
|
};
|
|
}, [
|
|
clearAnnounceTimeout,
|
|
targetDocument
|
|
]);
|
|
return announce;
|
|
};
|