|
1 | 1 | import matches from 'dom-helpers/matches';
|
2 | 2 | import qsa from 'dom-helpers/querySelectorAll';
|
| 3 | +import addEventListener from 'dom-helpers/addEventListener'; |
3 | 4 | import React, { useCallback, useRef, useEffect, useMemo } from 'react';
|
4 | 5 | import PropTypes from 'prop-types';
|
5 | 6 | import { useUncontrolledProp } from 'uncontrollable';
|
@@ -266,12 +267,28 @@ function Dropdown({
|
266 | 267 | if (next && next.focus) next.focus();
|
267 | 268 | }
|
268 | 269 | return;
|
269 | | - case 'Escape': |
270 | 270 | case 'Tab':
|
271 | | - if (key === 'Escape') { |
272 | | - event.preventDefault(); |
273 | | - event.stopPropagation(); |
274 | | - } |
| 271 | + // on keydown the target is the element being tabbed FROM, we need that |
| 272 | + // to know if this event is relevant to this dropdown (e.g. in this menu). |
| 273 | + // On `keyup` the target is the element being tagged TO which we use to check |
| 274 | + // if focus has left the menu |
| 275 | + addEventListener( |
| 276 | + document as any, |
| 277 | + 'keyup', |
| 278 | + (e) => { |
| 279 | + if ( |
| 280 | + (e.key === 'Tab' && !e.target) || |
| 281 | + !menuRef.current!.contains(e.target as HTMLElement) |
| 282 | + ) { |
| 283 | + onToggle(false, event); |
| 284 | + } |
| 285 | + }, |
| 286 | + { once: true }, |
| 287 | + ); |
| 288 | + break; |
| 289 | + case 'Escape': |
| 290 | + event.preventDefault(); |
| 291 | + event.stopPropagation(); |
275 | 292 |
|
276 | 293 | onToggle(false, event);
|
277 | 294 | break;
|
|
0 commit comments