···11-import { isFocusTarget } from './utils/focus';
11+import { isFocusTarget, getActive } from './utils/focus';
22import { useLayoutEffect } from './utils/react';
33import { click } from './utils/click';
44import { contains, isInputElement } from './utils/element';
···2626 function onKey(event: KeyboardEvent) {
2727 if (!element || event.defaultPrevented || event.isComposing) return;
28282929- const active = document.activeElement as HTMLElement;
2929+ const active = getActive();
3030 if (!isFocusTarget(element) || !contains(active, element)) {
3131 // Do nothing if the current item is not a target or not focused
3232 return;
+5-3
src/utils/click.ts
···11-import { clickableSelectors, focus } from './focus';
11+import { clickableSelectors, focus, getActive } from './focus';
22import { contains } from './element';
3344-export const click = (node: Element) => {
55- const activeElement = document.activeElement;
44+export const click = (node: Element | null) => {
55+ if (!node) return;
66+77+ const activeElement = getActive();
68 if (!activeElement || contains(node, activeElement)) {
79 let target: Element | null = node;
810 if (node.tagName === 'LABEL') {
+6-2
src/utils/element.ts
···1212 node.matches(excludeSelector) && node.getClientRects().length > 0;
13131414/** Returns whether an element accepts text input. */
1515-export const isInputElement = (node: Element): boolean =>
1616- node.matches(inputSelectors);
1515+export const isInputElement = (node: Element | null): boolean =>
1616+ !!node && node.matches(inputSelectors);
17171818export const contains = (
1919 owner: Element | EventTarget | null,
···2424 owner &&
2525 (owner === node || (owner as Element).contains(node as Element))
2626 );
2727+2828+/** Returns the root element of the input element */
2929+export const getRoot = (node: Element): HTMLElement =>
3030+ (node.getRootNode() || document.body) as HTMLElement;
+11-2
src/utils/focus.ts
···8282export const focus = (node: Element | null) => {
8383 if (node) {
8484 (node as HTMLElement).focus();
8585- } else if (document.activeElement) {
8686- (document.activeElement as HTMLElement).blur();
8585+ } else {
8686+ const active = getActive();
8787+ if (active) active.blur();
8788 }
8889};
9090+9191+/** Returns the currently active element, even if it’s contained in a shadow root. */
9292+export const getActive = (): HTMLElement | null => {
9393+ let element = document.activeElement;
9494+ while (element && element.shadowRoot)
9595+ element = element.shadowRoot.activeElement;
9696+ return element as HTMLElement | null;
9797+};