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