68 lines
2.6 KiB
JavaScript
68 lines
2.6 KiB
JavaScript
'use client';
|
|
import * as React from 'react';
|
|
import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';
|
|
import { useFocusedElementChange } from '@fluentui/react-tabster';
|
|
const findTreeItemRoot = (element)=>{
|
|
let parent = element.parentElement;
|
|
while(parent && parent.getAttribute('role') !== 'tree'){
|
|
parent = parent.parentElement;
|
|
}
|
|
return parent;
|
|
};
|
|
/**
|
|
* https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#kbd_roving_tabindex
|
|
*
|
|
* @internal
|
|
*/ export function useRovingTabIndex() {
|
|
const currentElementRef = React.useRef(null);
|
|
const walkerRef = React.useRef(null);
|
|
const { targetDocument } = useFluent();
|
|
useFocusedElementChange((element)=>{
|
|
if ((element === null || element === void 0 ? void 0 : element.getAttribute('role')) === 'treeitem' && walkerRef.current && walkerRef.current.root.contains(element)) {
|
|
const treeitemRoot = findTreeItemRoot(element);
|
|
if (walkerRef.current.root !== treeitemRoot) {
|
|
return;
|
|
}
|
|
rove(element);
|
|
}
|
|
});
|
|
const initialize = React.useCallback((walker)=>{
|
|
walkerRef.current = walker;
|
|
walker.currentElement = walker.root;
|
|
let tabbableChild = walker.firstChild((element)=>element.tabIndex === 0 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP);
|
|
walker.currentElement = walker.root;
|
|
tabbableChild !== null && tabbableChild !== void 0 ? tabbableChild : tabbableChild = walker.firstChild();
|
|
if (!tabbableChild) {
|
|
return;
|
|
}
|
|
tabbableChild.tabIndex = 0;
|
|
currentElementRef.current = tabbableChild;
|
|
let nextElement = null;
|
|
while((nextElement = walker.nextElement()) && nextElement !== tabbableChild){
|
|
nextElement.tabIndex = -1;
|
|
}
|
|
}, []);
|
|
const rove = React.useCallback((nextElement, focusOptions)=>{
|
|
if (!currentElementRef.current) {
|
|
return;
|
|
}
|
|
currentElementRef.current.tabIndex = -1;
|
|
nextElement.tabIndex = 0;
|
|
nextElement.focus(focusOptions);
|
|
currentElementRef.current = nextElement;
|
|
}, []);
|
|
const forceUpdate = React.useCallback(()=>{
|
|
if ((currentElementRef.current === null || !(targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.body.contains(currentElementRef.current))) && walkerRef.current) {
|
|
initialize(walkerRef.current);
|
|
}
|
|
}, [
|
|
targetDocument,
|
|
initialize
|
|
]);
|
|
return {
|
|
rove,
|
|
initialize,
|
|
forceUpdate
|
|
};
|
|
}
|