( root: Element, opts?: FocusManagerOptions, scope?: Element[] )
| 842 | * that matches all focusable/tabbable elements. |
| 843 | */ |
| 844 | export function getFocusableTreeWalker( |
| 845 | root: Element, |
| 846 | opts?: FocusManagerOptions, |
| 847 | scope?: Element[] |
| 848 | ): ShadowTreeWalker | TreeWalker { |
| 849 | let filter = opts?.tabbable ? isTabbable : isFocusable; |
| 850 | |
| 851 | // Ensure that root is an Element or fall back appropriately |
| 852 | let rootElement = root?.nodeType === Node.ELEMENT_NODE ? (root as Element) : null; |
| 853 | |
| 854 | // Determine the document to use |
| 855 | let doc = getOwnerDocument(rootElement); |
| 856 | |
| 857 | // Create a TreeWalker, ensuring the root is an Element or Document |
| 858 | let walker = createShadowTreeWalker(doc, root || doc, NodeFilter.SHOW_ELEMENT, { |
| 859 | acceptNode(node) { |
| 860 | // Skip nodes inside the starting node. |
| 861 | if (nodeContains(opts?.from, node)) { |
| 862 | return NodeFilter.FILTER_REJECT; |
| 863 | } |
| 864 | |
| 865 | if ( |
| 866 | opts?.tabbable && |
| 867 | (node as Element).tagName === 'INPUT' && |
| 868 | (node as HTMLInputElement).getAttribute('type') === 'radio' |
| 869 | ) { |
| 870 | // If the radio is in a form, we can get all the other radios by name |
| 871 | if (!isTabbableRadio(node as HTMLInputElement)) { |
| 872 | return NodeFilter.FILTER_REJECT; |
| 873 | } |
| 874 | // If the radio is in the same group as the current node and none are selected, we can skip it |
| 875 | if ( |
| 876 | (walker.currentNode as Element).tagName === 'INPUT' && |
| 877 | (walker.currentNode as HTMLInputElement).type === 'radio' && |
| 878 | (walker.currentNode as HTMLInputElement).name === (node as HTMLInputElement).name |
| 879 | ) { |
| 880 | return NodeFilter.FILTER_REJECT; |
| 881 | } |
| 882 | } |
| 883 | |
| 884 | if ( |
| 885 | filter(node as Element) && |
| 886 | (!scope || isElementInScope(node as Element, scope)) && |
| 887 | (!opts?.accept || opts.accept(node as Element)) |
| 888 | ) { |
| 889 | return NodeFilter.FILTER_ACCEPT; |
| 890 | } |
| 891 | |
| 892 | return NodeFilter.FILTER_SKIP; |
| 893 | } |
| 894 | }); |
| 895 | |
| 896 | if (opts?.from) { |
| 897 | walker.currentNode = opts.from; |
| 898 | } |
| 899 | |
| 900 | return walker; |
| 901 | } |
no test coverage detected