Outlook_Addin_LLM/node_modules/@fluentui/react-tabster/lib/focus/focusVisiblePolyfill.js

72 lines
2.5 KiB
JavaScript

import { isHTMLElement } from '@fluentui/react-utilities';
import { KEYBORG_FOCUSIN, createKeyborg, disposeKeyborg } from 'keyborg';
import { FOCUS_VISIBLE_ATTR } from './constants';
/**
* @internal
* @param scope - Applies the ponyfill to all DOM children
* @param targetWindow - window
*/ export function applyFocusVisiblePolyfill(scope, targetWindow) {
if (alreadyInScope(scope)) {
// Focus visible polyfill already applied at this scope
return ()=>undefined;
}
const state = {
current: undefined
};
const keyborg = createKeyborg(targetWindow);
function registerElementIfNavigating(el) {
if (keyborg.isNavigatingWithKeyboard() && isHTMLElement(el)) {
state.current = el;
el.setAttribute(FOCUS_VISIBLE_ATTR, '');
}
}
function disposeCurrentElement() {
if (state.current) {
state.current.removeAttribute(FOCUS_VISIBLE_ATTR);
state.current = undefined;
}
}
// When navigation mode changes remove the focus-visible selector
keyborg.subscribe((isNavigatingWithKeyboard)=>{
if (!isNavigatingWithKeyboard) {
disposeCurrentElement();
}
});
// Keyborg's focusin event is delegated so it's only registered once on the window
// and contains metadata about the focus event
const keyborgListener = (e)=>{
disposeCurrentElement();
const target = e.composedPath()[0];
registerElementIfNavigating(target);
};
// Make sure that when focus leaves the scope, the focus visible class is removed
const blurListener = (e)=>{
if (!e.relatedTarget || isHTMLElement(e.relatedTarget) && !scope.contains(e.relatedTarget)) {
disposeCurrentElement();
}
};
scope.addEventListener(KEYBORG_FOCUSIN, keyborgListener);
scope.addEventListener('focusout', blurListener);
scope.focusVisible = true;
if (scope.contains(targetWindow.document.activeElement)) {
registerElementIfNavigating(targetWindow.document.activeElement);
}
// Return disposer
return ()=>{
disposeCurrentElement();
scope.removeEventListener(KEYBORG_FOCUSIN, keyborgListener);
scope.removeEventListener('focusout', blurListener);
delete scope.focusVisible;
disposeKeyborg(keyborg);
};
}
function alreadyInScope(el) {
if (!el) {
return false;
}
if (el.focusVisible) {
return true;
}
return alreadyInScope(el === null || el === void 0 ? void 0 : el.parentElement);
}