@atlaskit/dropdown-menu 11.11.3 → 11.11.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/cjs/dropdown-menu.js +12 -7
- package/dist/cjs/internal/components/context.js +58 -0
- package/dist/cjs/internal/components/focus-manager.js +15 -3
- package/dist/cjs/internal/utils/handle-focus.js +12 -7
- package/dist/cjs/version.json +1 -1
- package/dist/es2019/dropdown-menu.js +24 -17
- package/dist/es2019/internal/components/context.js +45 -0
- package/dist/es2019/internal/components/focus-manager.js +17 -4
- package/dist/es2019/internal/utils/handle-focus.js +12 -7
- package/dist/es2019/version.json +1 -1
- package/dist/esm/dropdown-menu.js +13 -8
- package/dist/esm/internal/components/context.js +47 -0
- package/dist/esm/internal/components/focus-manager.js +16 -4
- package/dist/esm/internal/utils/handle-focus.js +12 -7
- package/dist/esm/version.json +1 -1
- package/dist/types/internal/components/context.d.ts +27 -0
- package/dist/types/internal/utils/handle-focus.d.ts +2 -1
- package/dist/types-ts4.5/internal/components/context.d.ts +27 -0
- package/dist/types-ts4.5/internal/utils/handle-focus.d.ts +2 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @atlaskit/dropdown-menu
|
|
2
2
|
|
|
3
|
+
## 11.11.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`9e84eaacfbe`](https://bitbucket.org/atlassian/atlassian-frontend/commits/9e84eaacfbe) - Nested dropdown menus are now accessible using arrow key navigation.
|
|
8
|
+
|
|
9
|
+
## 11.11.4
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [`303603a5525`](https://bitbucket.org/atlassian/atlassian-frontend/commits/303603a5525) - Internal changes.
|
|
14
|
+
|
|
3
15
|
## 11.11.3
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
|
@@ -14,6 +14,7 @@ var _react = _interopRequireWildcard(require("react"));
|
|
|
14
14
|
var _bindEventListener = require("bind-event-listener");
|
|
15
15
|
var _standardButton = _interopRequireDefault(require("@atlaskit/button/standard-button"));
|
|
16
16
|
var _keycodes = require("@atlaskit/ds-lib/keycodes");
|
|
17
|
+
var _mergeRefs = _interopRequireDefault(require("@atlaskit/ds-lib/merge-refs"));
|
|
17
18
|
var _noop = _interopRequireDefault(require("@atlaskit/ds-lib/noop"));
|
|
18
19
|
var _useControlled = _interopRequireDefault(require("@atlaskit/ds-lib/use-controlled"));
|
|
19
20
|
var _useFocusEvent = _interopRequireDefault(require("@atlaskit/ds-lib/use-focus-event"));
|
|
@@ -21,9 +22,11 @@ var _chevronDown = _interopRequireDefault(require("@atlaskit/icon/glyph/chevron-
|
|
|
21
22
|
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
22
23
|
var _popup = _interopRequireDefault(require("@atlaskit/popup"));
|
|
23
24
|
var _constants = require("@atlaskit/theme/constants");
|
|
25
|
+
var _context = require("./internal/components/context");
|
|
24
26
|
var _focusManager = _interopRequireDefault(require("./internal/components/focus-manager"));
|
|
25
27
|
var _menuWrapper = _interopRequireDefault(require("./internal/components/menu-wrapper"));
|
|
26
28
|
var _selectionStore = _interopRequireDefault(require("./internal/context/selection-store"));
|
|
29
|
+
var _useRegisterItemWithFocusManager = _interopRequireDefault(require("./internal/hooks/use-register-item-with-focus-manager"));
|
|
27
30
|
var _useGeneratedId = _interopRequireWildcard(require("./internal/utils/use-generated-id"));
|
|
28
31
|
var _excluded = ["ref"]; // eslint-disable-next-line @atlaskit/design-system/no-deprecated-imports
|
|
29
32
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
@@ -59,7 +62,6 @@ var getFallbackPlacements = function getFallbackPlacements(placement) {
|
|
|
59
62
|
return ["".concat(mainAxis, "-start"), "".concat(mainAxis, "-end"), "".concat(opposites[mainAxis]), "".concat(opposites[mainAxis], "-start"), "".concat(opposites[mainAxis], "-end"), "auto"];
|
|
60
63
|
}
|
|
61
64
|
};
|
|
62
|
-
var NestedContext = /*#__PURE__*/(0, _react.createContext)(false);
|
|
63
65
|
|
|
64
66
|
/**
|
|
65
67
|
* __Dropdown menu__
|
|
@@ -97,7 +99,7 @@ var DropdownMenu = function DropdownMenu(props) {
|
|
|
97
99
|
_useControlledState2 = (0, _slicedToArray2.default)(_useControlledState, 2),
|
|
98
100
|
isLocalOpen = _useControlledState2[0],
|
|
99
101
|
setLocalIsOpen = _useControlledState2[1];
|
|
100
|
-
var
|
|
102
|
+
var nestedLevel = (0, _react.useContext)(_context.NestedLevelContext);
|
|
101
103
|
var _useState = (0, _react.useState)(false),
|
|
102
104
|
_useState2 = (0, _slicedToArray2.default)(_useState, 2),
|
|
103
105
|
isTriggeredUsingKeyboard = _useState2[0],
|
|
@@ -171,6 +173,8 @@ var DropdownMenu = function DropdownMenu(props) {
|
|
|
171
173
|
});
|
|
172
174
|
}, [isFocused, isLocalOpen, handleTriggerClicked]);
|
|
173
175
|
var id = (0, _useGeneratedId.default)();
|
|
176
|
+
var isNested = nestedLevel > 0;
|
|
177
|
+
var itemRef = (0, _useRegisterItemWithFocusManager.default)();
|
|
174
178
|
return /*#__PURE__*/_react.default.createElement(_selectionStore.default, null, /*#__PURE__*/_react.default.createElement(_popup.default, {
|
|
175
179
|
id: isLocalOpen ? id : undefined,
|
|
176
180
|
shouldFlip: shouldFlip,
|
|
@@ -188,14 +192,14 @@ var DropdownMenu = function DropdownMenu(props) {
|
|
|
188
192
|
var ref = triggerProps.ref,
|
|
189
193
|
providedProps = (0, _objectWithoutProperties2.default)(triggerProps, _excluded);
|
|
190
194
|
return _trigger(_objectSpread(_objectSpread(_objectSpread({}, providedProps), bindFocus), {}, {
|
|
191
|
-
triggerRef: ref,
|
|
195
|
+
triggerRef: isNested ? (0, _mergeRefs.default)([ref, itemRef]) : ref,
|
|
192
196
|
isSelected: isLocalOpen,
|
|
193
197
|
onClick: handleTriggerClicked,
|
|
194
198
|
testId: testId && "".concat(testId, "--trigger")
|
|
195
199
|
}));
|
|
196
200
|
}
|
|
197
201
|
return /*#__PURE__*/_react.default.createElement(_standardButton.default, (0, _extends2.default)({}, bindFocus, {
|
|
198
|
-
ref: triggerProps.ref,
|
|
202
|
+
ref: isNested ? (0, _mergeRefs.default)([triggerProps.ref, itemRef]) : triggerProps.ref,
|
|
199
203
|
"aria-controls": triggerProps['aria-controls'],
|
|
200
204
|
"aria-expanded": triggerProps['aria-expanded'],
|
|
201
205
|
"aria-haspopup": triggerProps['aria-haspopup'],
|
|
@@ -211,7 +215,7 @@ var DropdownMenu = function DropdownMenu(props) {
|
|
|
211
215
|
content: function content(_ref) {
|
|
212
216
|
var setInitialFocusRef = _ref.setInitialFocusRef,
|
|
213
217
|
update = _ref.update;
|
|
214
|
-
|
|
218
|
+
var content = /*#__PURE__*/_react.default.createElement(_focusManager.default, null, /*#__PURE__*/_react.default.createElement(_menuWrapper.default, {
|
|
215
219
|
spacing: spacing,
|
|
216
220
|
maxHeight: MAX_HEIGHT,
|
|
217
221
|
maxWidth: 800,
|
|
@@ -220,9 +224,10 @@ var DropdownMenu = function DropdownMenu(props) {
|
|
|
220
224
|
isLoading: isLoading,
|
|
221
225
|
statusLabel: statusLabel,
|
|
222
226
|
setInitialFocusRef: isTriggeredUsingKeyboard || autoFocus ? setInitialFocusRef : undefined
|
|
223
|
-
},
|
|
224
|
-
value:
|
|
227
|
+
}, /*#__PURE__*/_react.default.createElement(_context.NestedLevelContext.Provider, {
|
|
228
|
+
value: nestedLevel + 1
|
|
225
229
|
}, children)));
|
|
230
|
+
return isNested ? content : /*#__PURE__*/_react.default.createElement(_context.TrackLevelProvider, null, content);
|
|
226
231
|
}
|
|
227
232
|
}));
|
|
228
233
|
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _typeof = require("@babel/runtime/helpers/typeof");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.TrackMaxLevelContext = exports.TrackLevelProvider = exports.NestedLevelContext = void 0;
|
|
8
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
9
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
10
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
11
|
+
/**
|
|
12
|
+
*
|
|
13
|
+
* @internal
|
|
14
|
+
* Context which maintains the current level of dropdown menu if it is nested
|
|
15
|
+
* Default is 0
|
|
16
|
+
*
|
|
17
|
+
*/
|
|
18
|
+
var NestedLevelContext = /*#__PURE__*/(0, _react.createContext)(0);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
*
|
|
22
|
+
* @internal
|
|
23
|
+
* Context which maintains the maximun level of dropdown menu if it is nested
|
|
24
|
+
* Default is 0
|
|
25
|
+
*
|
|
26
|
+
*/
|
|
27
|
+
exports.NestedLevelContext = NestedLevelContext;
|
|
28
|
+
var TrackMaxLevelContext = /*#__PURE__*/(0, _react.createContext)({
|
|
29
|
+
maxLevelRef: {
|
|
30
|
+
current: 0
|
|
31
|
+
},
|
|
32
|
+
setMaxLevel: function setMaxLevel() {}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
*
|
|
37
|
+
* @internal
|
|
38
|
+
* Context provider which maintains the maximun level of dropdown menu if it is nested
|
|
39
|
+
*
|
|
40
|
+
*/
|
|
41
|
+
exports.TrackMaxLevelContext = TrackMaxLevelContext;
|
|
42
|
+
var TrackLevelProvider = function TrackLevelProvider(_ref) {
|
|
43
|
+
var children = _ref.children;
|
|
44
|
+
var maxLevelRef = (0, _react.useRef)(0);
|
|
45
|
+
var value = (0, _react.useMemo)(function () {
|
|
46
|
+
return {
|
|
47
|
+
maxLevelRef: maxLevelRef,
|
|
48
|
+
setMaxLevel: function setMaxLevel(level) {
|
|
49
|
+
var isMin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
50
|
+
maxLevelRef.current = isMin ? Math.min(maxLevelRef.current, level) : level;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}, [maxLevelRef]);
|
|
54
|
+
return /*#__PURE__*/_react.default.createElement(TrackMaxLevelContext.Provider, {
|
|
55
|
+
value: value
|
|
56
|
+
}, children);
|
|
57
|
+
};
|
|
58
|
+
exports.TrackLevelProvider = TrackLevelProvider;
|
|
@@ -10,6 +10,7 @@ var _react = _interopRequireWildcard(require("react"));
|
|
|
10
10
|
var _bindEventListener = require("bind-event-listener");
|
|
11
11
|
var _noop = _interopRequireDefault(require("@atlaskit/ds-lib/noop"));
|
|
12
12
|
var _handleFocus = _interopRequireDefault(require("../utils/handle-focus"));
|
|
13
|
+
var _context = require("./context");
|
|
13
14
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
14
15
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
15
16
|
/**
|
|
@@ -37,13 +38,24 @@ var FocusManager = function FocusManager(_ref) {
|
|
|
37
38
|
menuItemRefs.current.push(ref);
|
|
38
39
|
}
|
|
39
40
|
}, []);
|
|
40
|
-
|
|
41
|
+
var nestedLevel = (0, _react.useContext)(_context.NestedLevelContext);
|
|
42
|
+
var _useContext = (0, _react.useContext)(_context.TrackMaxLevelContext),
|
|
43
|
+
maxLevelRef = _useContext.maxLevelRef,
|
|
44
|
+
setMaxLevel = _useContext.setMaxLevel;
|
|
41
45
|
// Intentionally rebinding on each render
|
|
42
46
|
(0, _react.useEffect)(function () {
|
|
43
|
-
|
|
47
|
+
var prevLevel = nestedLevel - 1;
|
|
48
|
+
setMaxLevel(nestedLevel);
|
|
49
|
+
var unbind = (0, _bindEventListener.bind)(window, {
|
|
44
50
|
type: 'keydown',
|
|
45
|
-
listener: (0, _handleFocus.default)(menuItemRefs.current)
|
|
51
|
+
listener: (0, _handleFocus.default)(menuItemRefs.current, nestedLevel, maxLevelRef)
|
|
46
52
|
});
|
|
53
|
+
return function () {
|
|
54
|
+
// Always get the minimun level when multiple levels of menu are closed
|
|
55
|
+
// If the stored level is smaller, we won't update it
|
|
56
|
+
setMaxLevel(prevLevel, true);
|
|
57
|
+
unbind();
|
|
58
|
+
};
|
|
47
59
|
});
|
|
48
60
|
var contextValue = {
|
|
49
61
|
menuItemRefs: menuItemRefs.current,
|
|
@@ -18,7 +18,7 @@ var actionMap = (_actionMap = {}, (0, _defineProperty2.default)(_actionMap, _key
|
|
|
18
18
|
*/
|
|
19
19
|
var getNextFocusableElement = function getNextFocusableElement(refs, currentFocusedIdx) {
|
|
20
20
|
while (currentFocusedIdx + 1 < refs.length) {
|
|
21
|
-
var isDisabled = refs[currentFocusedIdx + 1].
|
|
21
|
+
var isDisabled = refs[currentFocusedIdx + 1].hasAttribute('disabled');
|
|
22
22
|
if (!isDisabled) {
|
|
23
23
|
return refs[currentFocusedIdx + 1];
|
|
24
24
|
}
|
|
@@ -34,19 +34,24 @@ var getNextFocusableElement = function getNextFocusableElement(refs, currentFocu
|
|
|
34
34
|
*/
|
|
35
35
|
var getPrevFocusableElement = function getPrevFocusableElement(refs, currentFocusedIdx) {
|
|
36
36
|
while (currentFocusedIdx > 0) {
|
|
37
|
-
var isDisabled = refs[currentFocusedIdx - 1].
|
|
37
|
+
var isDisabled = refs[currentFocusedIdx - 1].hasAttribute('disabled');
|
|
38
38
|
if (!isDisabled) {
|
|
39
39
|
return refs[currentFocusedIdx - 1];
|
|
40
40
|
}
|
|
41
41
|
currentFocusedIdx--;
|
|
42
42
|
}
|
|
43
43
|
};
|
|
44
|
-
function handleFocus(refs) {
|
|
44
|
+
function handleFocus(refs, nestedLevel, maxLevelRef) {
|
|
45
45
|
return function (e) {
|
|
46
46
|
var currentFocusedIdx = refs.findIndex(function (el) {
|
|
47
47
|
var _document$activeEleme;
|
|
48
48
|
return (_document$activeEleme = document.activeElement) === null || _document$activeEleme === void 0 ? void 0 : _document$activeEleme.isSameNode(el);
|
|
49
49
|
});
|
|
50
|
+
if (nestedLevel < maxLevelRef.current) {
|
|
51
|
+
// if it is a nested dropdown and the level of the given dropdown is not the current level,
|
|
52
|
+
// we don't need to have focus on it
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
50
55
|
var action = actionMap[e.key];
|
|
51
56
|
switch (action) {
|
|
52
57
|
case 'next':
|
|
@@ -54,7 +59,7 @@ function handleFocus(refs) {
|
|
|
54
59
|
e.preventDefault();
|
|
55
60
|
if (currentFocusedIdx < refs.length - 1) {
|
|
56
61
|
var _nextFocusableElement = getNextFocusableElement(refs, currentFocusedIdx);
|
|
57
|
-
_nextFocusableElement
|
|
62
|
+
_nextFocusableElement === null || _nextFocusableElement === void 0 ? void 0 : _nextFocusableElement.focus();
|
|
58
63
|
}
|
|
59
64
|
break;
|
|
60
65
|
case 'prev':
|
|
@@ -62,20 +67,20 @@ function handleFocus(refs) {
|
|
|
62
67
|
e.preventDefault();
|
|
63
68
|
if (currentFocusedIdx > 0) {
|
|
64
69
|
var _prevFocusableElement = getPrevFocusableElement(refs, currentFocusedIdx);
|
|
65
|
-
_prevFocusableElement
|
|
70
|
+
_prevFocusableElement === null || _prevFocusableElement === void 0 ? void 0 : _prevFocusableElement.focus();
|
|
66
71
|
}
|
|
67
72
|
break;
|
|
68
73
|
case 'first':
|
|
69
74
|
e.preventDefault();
|
|
70
75
|
// Search for first non-disabled element if first element is disabled
|
|
71
76
|
var nextFocusableElement = getNextFocusableElement(refs, -1);
|
|
72
|
-
nextFocusableElement
|
|
77
|
+
nextFocusableElement === null || nextFocusableElement === void 0 ? void 0 : nextFocusableElement.focus();
|
|
73
78
|
break;
|
|
74
79
|
case 'last':
|
|
75
80
|
e.preventDefault();
|
|
76
81
|
// Search for last non-disabled element if last element is disabled
|
|
77
82
|
var prevFocusableElement = getPrevFocusableElement(refs, refs.length);
|
|
78
|
-
prevFocusableElement
|
|
83
|
+
prevFocusableElement === null || prevFocusableElement === void 0 ? void 0 : prevFocusableElement.focus();
|
|
79
84
|
break;
|
|
80
85
|
default:
|
|
81
86
|
return;
|
package/dist/cjs/version.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
-
import React, {
|
|
2
|
+
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
|
3
3
|
import { bind } from 'bind-event-listener';
|
|
4
4
|
import Button from '@atlaskit/button/standard-button';
|
|
5
5
|
import { KEY_DOWN } from '@atlaskit/ds-lib/keycodes';
|
|
6
|
+
import mergeRefs from '@atlaskit/ds-lib/merge-refs';
|
|
6
7
|
import noop from '@atlaskit/ds-lib/noop';
|
|
7
8
|
import useControlledState from '@atlaskit/ds-lib/use-controlled';
|
|
8
9
|
import useFocus from '@atlaskit/ds-lib/use-focus-event';
|
|
@@ -11,9 +12,11 @@ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
|
|
|
11
12
|
import Popup from '@atlaskit/popup';
|
|
12
13
|
// eslint-disable-next-line @atlaskit/design-system/no-deprecated-imports
|
|
13
14
|
import { gridSize as gridSizeFn, layers } from '@atlaskit/theme/constants';
|
|
15
|
+
import { NestedLevelContext, TrackLevelProvider } from './internal/components/context';
|
|
14
16
|
import FocusManager from './internal/components/focus-manager';
|
|
15
17
|
import MenuWrapper from './internal/components/menu-wrapper';
|
|
16
18
|
import SelectionStore from './internal/context/selection-store';
|
|
19
|
+
import useRegisterItemWithFocusManager from './internal/hooks/use-register-item-with-focus-manager';
|
|
17
20
|
import useGeneratedId, { PREFIX } from './internal/utils/use-generated-id';
|
|
18
21
|
const gridSize = gridSizeFn();
|
|
19
22
|
const MAX_HEIGHT = `calc(100vh - ${gridSize * 2}px)`;
|
|
@@ -44,7 +47,6 @@ const getFallbackPlacements = placement => {
|
|
|
44
47
|
return [`${mainAxis}-start`, `${mainAxis}-end`, `${opposites[mainAxis]}`, `${opposites[mainAxis]}-start`, `${opposites[mainAxis]}-end`, `auto`];
|
|
45
48
|
}
|
|
46
49
|
};
|
|
47
|
-
const NestedContext = /*#__PURE__*/createContext(false);
|
|
48
50
|
|
|
49
51
|
/**
|
|
50
52
|
* __Dropdown menu__
|
|
@@ -72,7 +74,7 @@ const DropdownMenu = props => {
|
|
|
72
74
|
zIndex = layers.modal()
|
|
73
75
|
} = props;
|
|
74
76
|
const [isLocalOpen, setLocalIsOpen] = useControlledState(isOpen, () => defaultOpen);
|
|
75
|
-
const
|
|
77
|
+
const nestedLevel = useContext(NestedLevelContext);
|
|
76
78
|
const [isTriggeredUsingKeyboard, setTriggeredUsingKeyboard] = useState(false);
|
|
77
79
|
const fallbackPlacements = useMemo(() => getFallbackPlacements(placement), [placement]);
|
|
78
80
|
const handleTriggerClicked = useCallback(
|
|
@@ -144,6 +146,8 @@ const DropdownMenu = props => {
|
|
|
144
146
|
});
|
|
145
147
|
}, [isFocused, isLocalOpen, handleTriggerClicked]);
|
|
146
148
|
const id = useGeneratedId();
|
|
149
|
+
const isNested = nestedLevel > 0;
|
|
150
|
+
const itemRef = useRegisterItemWithFocusManager();
|
|
147
151
|
return /*#__PURE__*/React.createElement(SelectionStore, null, /*#__PURE__*/React.createElement(Popup, {
|
|
148
152
|
id: isLocalOpen ? id : undefined,
|
|
149
153
|
shouldFlip: shouldFlip,
|
|
@@ -165,14 +169,14 @@ const DropdownMenu = props => {
|
|
|
165
169
|
return trigger({
|
|
166
170
|
...providedProps,
|
|
167
171
|
...bindFocus,
|
|
168
|
-
triggerRef: ref,
|
|
172
|
+
triggerRef: isNested ? mergeRefs([ref, itemRef]) : ref,
|
|
169
173
|
isSelected: isLocalOpen,
|
|
170
174
|
onClick: handleTriggerClicked,
|
|
171
175
|
testId: testId && `${testId}--trigger`
|
|
172
176
|
});
|
|
173
177
|
}
|
|
174
178
|
return /*#__PURE__*/React.createElement(Button, _extends({}, bindFocus, {
|
|
175
|
-
ref: triggerProps.ref,
|
|
179
|
+
ref: isNested ? mergeRefs([triggerProps.ref, itemRef]) : triggerProps.ref,
|
|
176
180
|
"aria-controls": triggerProps['aria-controls'],
|
|
177
181
|
"aria-expanded": triggerProps['aria-expanded'],
|
|
178
182
|
"aria-haspopup": triggerProps['aria-haspopup'],
|
|
@@ -188,18 +192,21 @@ const DropdownMenu = props => {
|
|
|
188
192
|
content: ({
|
|
189
193
|
setInitialFocusRef,
|
|
190
194
|
update
|
|
191
|
-
}) =>
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
195
|
+
}) => {
|
|
196
|
+
const content = /*#__PURE__*/React.createElement(FocusManager, null, /*#__PURE__*/React.createElement(MenuWrapper, {
|
|
197
|
+
spacing: spacing,
|
|
198
|
+
maxHeight: MAX_HEIGHT,
|
|
199
|
+
maxWidth: 800,
|
|
200
|
+
onClose: handleOnClose,
|
|
201
|
+
onUpdate: update,
|
|
202
|
+
isLoading: isLoading,
|
|
203
|
+
statusLabel: statusLabel,
|
|
204
|
+
setInitialFocusRef: isTriggeredUsingKeyboard || autoFocus ? setInitialFocusRef : undefined
|
|
205
|
+
}, /*#__PURE__*/React.createElement(NestedLevelContext.Provider, {
|
|
206
|
+
value: nestedLevel + 1
|
|
207
|
+
}, children)));
|
|
208
|
+
return isNested ? content : /*#__PURE__*/React.createElement(TrackLevelProvider, null, content);
|
|
209
|
+
}
|
|
203
210
|
}));
|
|
204
211
|
};
|
|
205
212
|
export default DropdownMenu;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import React, { createContext, useMemo, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @internal
|
|
6
|
+
* Context which maintains the current level of dropdown menu if it is nested
|
|
7
|
+
* Default is 0
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
10
|
+
export const NestedLevelContext = /*#__PURE__*/createContext(0);
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
*
|
|
14
|
+
* @internal
|
|
15
|
+
* Context which maintains the maximun level of dropdown menu if it is nested
|
|
16
|
+
* Default is 0
|
|
17
|
+
*
|
|
18
|
+
*/
|
|
19
|
+
export const TrackMaxLevelContext = /*#__PURE__*/createContext({
|
|
20
|
+
maxLevelRef: {
|
|
21
|
+
current: 0
|
|
22
|
+
},
|
|
23
|
+
setMaxLevel: () => {}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
*
|
|
28
|
+
* @internal
|
|
29
|
+
* Context provider which maintains the maximun level of dropdown menu if it is nested
|
|
30
|
+
*
|
|
31
|
+
*/
|
|
32
|
+
export const TrackLevelProvider = ({
|
|
33
|
+
children
|
|
34
|
+
}) => {
|
|
35
|
+
const maxLevelRef = useRef(0);
|
|
36
|
+
const value = useMemo(() => ({
|
|
37
|
+
maxLevelRef,
|
|
38
|
+
setMaxLevel: (level, isMin = false) => {
|
|
39
|
+
maxLevelRef.current = isMin ? Math.min(maxLevelRef.current, level) : level;
|
|
40
|
+
}
|
|
41
|
+
}), [maxLevelRef]);
|
|
42
|
+
return /*#__PURE__*/React.createElement(TrackMaxLevelContext.Provider, {
|
|
43
|
+
value: value
|
|
44
|
+
}, children);
|
|
45
|
+
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import React, { createContext, useCallback, useEffect, useRef } from 'react';
|
|
1
|
+
import React, { createContext, useCallback, useContext, useEffect, useRef } from 'react';
|
|
2
2
|
import { bind } from 'bind-event-listener';
|
|
3
3
|
import __noop from '@atlaskit/ds-lib/noop';
|
|
4
4
|
import handleFocus from '../utils/handle-focus';
|
|
5
|
+
import { NestedLevelContext, TrackMaxLevelContext } from './context';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
*
|
|
@@ -28,13 +29,25 @@ const FocusManager = ({
|
|
|
28
29
|
menuItemRefs.current.push(ref);
|
|
29
30
|
}
|
|
30
31
|
}, []);
|
|
31
|
-
|
|
32
|
+
const nestedLevel = useContext(NestedLevelContext);
|
|
33
|
+
const {
|
|
34
|
+
maxLevelRef,
|
|
35
|
+
setMaxLevel
|
|
36
|
+
} = useContext(TrackMaxLevelContext);
|
|
32
37
|
// Intentionally rebinding on each render
|
|
33
38
|
useEffect(() => {
|
|
34
|
-
|
|
39
|
+
const prevLevel = nestedLevel - 1;
|
|
40
|
+
setMaxLevel(nestedLevel);
|
|
41
|
+
const unbind = bind(window, {
|
|
35
42
|
type: 'keydown',
|
|
36
|
-
listener: handleFocus(menuItemRefs.current)
|
|
43
|
+
listener: handleFocus(menuItemRefs.current, nestedLevel, maxLevelRef)
|
|
37
44
|
});
|
|
45
|
+
return () => {
|
|
46
|
+
// Always get the minimun level when multiple levels of menu are closed
|
|
47
|
+
// If the stored level is smaller, we won't update it
|
|
48
|
+
setMaxLevel(prevLevel, true);
|
|
49
|
+
unbind();
|
|
50
|
+
};
|
|
38
51
|
});
|
|
39
52
|
const contextValue = {
|
|
40
53
|
menuItemRefs: menuItemRefs.current,
|
|
@@ -14,7 +14,7 @@ const actionMap = {
|
|
|
14
14
|
*/
|
|
15
15
|
const getNextFocusableElement = (refs, currentFocusedIdx) => {
|
|
16
16
|
while (currentFocusedIdx + 1 < refs.length) {
|
|
17
|
-
const isDisabled = refs[currentFocusedIdx + 1].
|
|
17
|
+
const isDisabled = refs[currentFocusedIdx + 1].hasAttribute('disabled');
|
|
18
18
|
if (!isDisabled) {
|
|
19
19
|
return refs[currentFocusedIdx + 1];
|
|
20
20
|
}
|
|
@@ -30,19 +30,24 @@ const getNextFocusableElement = (refs, currentFocusedIdx) => {
|
|
|
30
30
|
*/
|
|
31
31
|
const getPrevFocusableElement = (refs, currentFocusedIdx) => {
|
|
32
32
|
while (currentFocusedIdx > 0) {
|
|
33
|
-
const isDisabled = refs[currentFocusedIdx - 1].
|
|
33
|
+
const isDisabled = refs[currentFocusedIdx - 1].hasAttribute('disabled');
|
|
34
34
|
if (!isDisabled) {
|
|
35
35
|
return refs[currentFocusedIdx - 1];
|
|
36
36
|
}
|
|
37
37
|
currentFocusedIdx--;
|
|
38
38
|
}
|
|
39
39
|
};
|
|
40
|
-
export default function handleFocus(refs) {
|
|
40
|
+
export default function handleFocus(refs, nestedLevel, maxLevelRef) {
|
|
41
41
|
return e => {
|
|
42
42
|
const currentFocusedIdx = refs.findIndex(el => {
|
|
43
43
|
var _document$activeEleme;
|
|
44
44
|
return (_document$activeEleme = document.activeElement) === null || _document$activeEleme === void 0 ? void 0 : _document$activeEleme.isSameNode(el);
|
|
45
45
|
});
|
|
46
|
+
if (nestedLevel < maxLevelRef.current) {
|
|
47
|
+
// if it is a nested dropdown and the level of the given dropdown is not the current level,
|
|
48
|
+
// we don't need to have focus on it
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
46
51
|
const action = actionMap[e.key];
|
|
47
52
|
switch (action) {
|
|
48
53
|
case 'next':
|
|
@@ -50,7 +55,7 @@ export default function handleFocus(refs) {
|
|
|
50
55
|
e.preventDefault();
|
|
51
56
|
if (currentFocusedIdx < refs.length - 1) {
|
|
52
57
|
const nextFocusableElement = getNextFocusableElement(refs, currentFocusedIdx);
|
|
53
|
-
nextFocusableElement
|
|
58
|
+
nextFocusableElement === null || nextFocusableElement === void 0 ? void 0 : nextFocusableElement.focus();
|
|
54
59
|
}
|
|
55
60
|
break;
|
|
56
61
|
case 'prev':
|
|
@@ -58,20 +63,20 @@ export default function handleFocus(refs) {
|
|
|
58
63
|
e.preventDefault();
|
|
59
64
|
if (currentFocusedIdx > 0) {
|
|
60
65
|
const prevFocusableElement = getPrevFocusableElement(refs, currentFocusedIdx);
|
|
61
|
-
prevFocusableElement
|
|
66
|
+
prevFocusableElement === null || prevFocusableElement === void 0 ? void 0 : prevFocusableElement.focus();
|
|
62
67
|
}
|
|
63
68
|
break;
|
|
64
69
|
case 'first':
|
|
65
70
|
e.preventDefault();
|
|
66
71
|
// Search for first non-disabled element if first element is disabled
|
|
67
72
|
const nextFocusableElement = getNextFocusableElement(refs, -1);
|
|
68
|
-
nextFocusableElement
|
|
73
|
+
nextFocusableElement === null || nextFocusableElement === void 0 ? void 0 : nextFocusableElement.focus();
|
|
69
74
|
break;
|
|
70
75
|
case 'last':
|
|
71
76
|
e.preventDefault();
|
|
72
77
|
// Search for last non-disabled element if last element is disabled
|
|
73
78
|
const prevFocusableElement = getPrevFocusableElement(refs, refs.length);
|
|
74
|
-
prevFocusableElement
|
|
79
|
+
prevFocusableElement === null || prevFocusableElement === void 0 ? void 0 : prevFocusableElement.focus();
|
|
75
80
|
break;
|
|
76
81
|
default:
|
|
77
82
|
return;
|
package/dist/es2019/version.json
CHANGED
|
@@ -5,10 +5,11 @@ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
|
5
5
|
var _excluded = ["ref"];
|
|
6
6
|
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
7
7
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
8
|
-
import React, {
|
|
8
|
+
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
|
9
9
|
import { bind } from 'bind-event-listener';
|
|
10
10
|
import Button from '@atlaskit/button/standard-button';
|
|
11
11
|
import { KEY_DOWN } from '@atlaskit/ds-lib/keycodes';
|
|
12
|
+
import mergeRefs from '@atlaskit/ds-lib/merge-refs';
|
|
12
13
|
import noop from '@atlaskit/ds-lib/noop';
|
|
13
14
|
import useControlledState from '@atlaskit/ds-lib/use-controlled';
|
|
14
15
|
import useFocus from '@atlaskit/ds-lib/use-focus-event';
|
|
@@ -17,9 +18,11 @@ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
|
|
|
17
18
|
import Popup from '@atlaskit/popup';
|
|
18
19
|
// eslint-disable-next-line @atlaskit/design-system/no-deprecated-imports
|
|
19
20
|
import { gridSize as gridSizeFn, layers } from '@atlaskit/theme/constants';
|
|
21
|
+
import { NestedLevelContext, TrackLevelProvider } from './internal/components/context';
|
|
20
22
|
import FocusManager from './internal/components/focus-manager';
|
|
21
23
|
import MenuWrapper from './internal/components/menu-wrapper';
|
|
22
24
|
import SelectionStore from './internal/context/selection-store';
|
|
25
|
+
import useRegisterItemWithFocusManager from './internal/hooks/use-register-item-with-focus-manager';
|
|
23
26
|
import useGeneratedId, { PREFIX } from './internal/utils/use-generated-id';
|
|
24
27
|
var gridSize = gridSizeFn();
|
|
25
28
|
var MAX_HEIGHT = "calc(100vh - ".concat(gridSize * 2, "px)");
|
|
@@ -50,7 +53,6 @@ var getFallbackPlacements = function getFallbackPlacements(placement) {
|
|
|
50
53
|
return ["".concat(mainAxis, "-start"), "".concat(mainAxis, "-end"), "".concat(opposites[mainAxis]), "".concat(opposites[mainAxis], "-start"), "".concat(opposites[mainAxis], "-end"), "auto"];
|
|
51
54
|
}
|
|
52
55
|
};
|
|
53
|
-
var NestedContext = /*#__PURE__*/createContext(false);
|
|
54
56
|
|
|
55
57
|
/**
|
|
56
58
|
* __Dropdown menu__
|
|
@@ -88,7 +90,7 @@ var DropdownMenu = function DropdownMenu(props) {
|
|
|
88
90
|
_useControlledState2 = _slicedToArray(_useControlledState, 2),
|
|
89
91
|
isLocalOpen = _useControlledState2[0],
|
|
90
92
|
setLocalIsOpen = _useControlledState2[1];
|
|
91
|
-
var
|
|
93
|
+
var nestedLevel = useContext(NestedLevelContext);
|
|
92
94
|
var _useState = useState(false),
|
|
93
95
|
_useState2 = _slicedToArray(_useState, 2),
|
|
94
96
|
isTriggeredUsingKeyboard = _useState2[0],
|
|
@@ -162,6 +164,8 @@ var DropdownMenu = function DropdownMenu(props) {
|
|
|
162
164
|
});
|
|
163
165
|
}, [isFocused, isLocalOpen, handleTriggerClicked]);
|
|
164
166
|
var id = useGeneratedId();
|
|
167
|
+
var isNested = nestedLevel > 0;
|
|
168
|
+
var itemRef = useRegisterItemWithFocusManager();
|
|
165
169
|
return /*#__PURE__*/React.createElement(SelectionStore, null, /*#__PURE__*/React.createElement(Popup, {
|
|
166
170
|
id: isLocalOpen ? id : undefined,
|
|
167
171
|
shouldFlip: shouldFlip,
|
|
@@ -179,14 +183,14 @@ var DropdownMenu = function DropdownMenu(props) {
|
|
|
179
183
|
var ref = triggerProps.ref,
|
|
180
184
|
providedProps = _objectWithoutProperties(triggerProps, _excluded);
|
|
181
185
|
return _trigger(_objectSpread(_objectSpread(_objectSpread({}, providedProps), bindFocus), {}, {
|
|
182
|
-
triggerRef: ref,
|
|
186
|
+
triggerRef: isNested ? mergeRefs([ref, itemRef]) : ref,
|
|
183
187
|
isSelected: isLocalOpen,
|
|
184
188
|
onClick: handleTriggerClicked,
|
|
185
189
|
testId: testId && "".concat(testId, "--trigger")
|
|
186
190
|
}));
|
|
187
191
|
}
|
|
188
192
|
return /*#__PURE__*/React.createElement(Button, _extends({}, bindFocus, {
|
|
189
|
-
ref: triggerProps.ref,
|
|
193
|
+
ref: isNested ? mergeRefs([triggerProps.ref, itemRef]) : triggerProps.ref,
|
|
190
194
|
"aria-controls": triggerProps['aria-controls'],
|
|
191
195
|
"aria-expanded": triggerProps['aria-expanded'],
|
|
192
196
|
"aria-haspopup": triggerProps['aria-haspopup'],
|
|
@@ -202,7 +206,7 @@ var DropdownMenu = function DropdownMenu(props) {
|
|
|
202
206
|
content: function content(_ref) {
|
|
203
207
|
var setInitialFocusRef = _ref.setInitialFocusRef,
|
|
204
208
|
update = _ref.update;
|
|
205
|
-
|
|
209
|
+
var content = /*#__PURE__*/React.createElement(FocusManager, null, /*#__PURE__*/React.createElement(MenuWrapper, {
|
|
206
210
|
spacing: spacing,
|
|
207
211
|
maxHeight: MAX_HEIGHT,
|
|
208
212
|
maxWidth: 800,
|
|
@@ -211,9 +215,10 @@ var DropdownMenu = function DropdownMenu(props) {
|
|
|
211
215
|
isLoading: isLoading,
|
|
212
216
|
statusLabel: statusLabel,
|
|
213
217
|
setInitialFocusRef: isTriggeredUsingKeyboard || autoFocus ? setInitialFocusRef : undefined
|
|
214
|
-
},
|
|
215
|
-
value:
|
|
218
|
+
}, /*#__PURE__*/React.createElement(NestedLevelContext.Provider, {
|
|
219
|
+
value: nestedLevel + 1
|
|
216
220
|
}, children)));
|
|
221
|
+
return isNested ? content : /*#__PURE__*/React.createElement(TrackLevelProvider, null, content);
|
|
217
222
|
}
|
|
218
223
|
}));
|
|
219
224
|
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React, { createContext, useMemo, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @internal
|
|
6
|
+
* Context which maintains the current level of dropdown menu if it is nested
|
|
7
|
+
* Default is 0
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
10
|
+
export var NestedLevelContext = /*#__PURE__*/createContext(0);
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
*
|
|
14
|
+
* @internal
|
|
15
|
+
* Context which maintains the maximun level of dropdown menu if it is nested
|
|
16
|
+
* Default is 0
|
|
17
|
+
*
|
|
18
|
+
*/
|
|
19
|
+
export var TrackMaxLevelContext = /*#__PURE__*/createContext({
|
|
20
|
+
maxLevelRef: {
|
|
21
|
+
current: 0
|
|
22
|
+
},
|
|
23
|
+
setMaxLevel: function setMaxLevel() {}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
*
|
|
28
|
+
* @internal
|
|
29
|
+
* Context provider which maintains the maximun level of dropdown menu if it is nested
|
|
30
|
+
*
|
|
31
|
+
*/
|
|
32
|
+
export var TrackLevelProvider = function TrackLevelProvider(_ref) {
|
|
33
|
+
var children = _ref.children;
|
|
34
|
+
var maxLevelRef = useRef(0);
|
|
35
|
+
var value = useMemo(function () {
|
|
36
|
+
return {
|
|
37
|
+
maxLevelRef: maxLevelRef,
|
|
38
|
+
setMaxLevel: function setMaxLevel(level) {
|
|
39
|
+
var isMin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
40
|
+
maxLevelRef.current = isMin ? Math.min(maxLevelRef.current, level) : level;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}, [maxLevelRef]);
|
|
44
|
+
return /*#__PURE__*/React.createElement(TrackMaxLevelContext.Provider, {
|
|
45
|
+
value: value
|
|
46
|
+
}, children);
|
|
47
|
+
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import React, { createContext, useCallback, useEffect, useRef } from 'react';
|
|
1
|
+
import React, { createContext, useCallback, useContext, useEffect, useRef } from 'react';
|
|
2
2
|
import { bind } from 'bind-event-listener';
|
|
3
3
|
import __noop from '@atlaskit/ds-lib/noop';
|
|
4
4
|
import handleFocus from '../utils/handle-focus';
|
|
5
|
+
import { NestedLevelContext, TrackMaxLevelContext } from './context';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
*
|
|
@@ -27,13 +28,24 @@ var FocusManager = function FocusManager(_ref) {
|
|
|
27
28
|
menuItemRefs.current.push(ref);
|
|
28
29
|
}
|
|
29
30
|
}, []);
|
|
30
|
-
|
|
31
|
+
var nestedLevel = useContext(NestedLevelContext);
|
|
32
|
+
var _useContext = useContext(TrackMaxLevelContext),
|
|
33
|
+
maxLevelRef = _useContext.maxLevelRef,
|
|
34
|
+
setMaxLevel = _useContext.setMaxLevel;
|
|
31
35
|
// Intentionally rebinding on each render
|
|
32
36
|
useEffect(function () {
|
|
33
|
-
|
|
37
|
+
var prevLevel = nestedLevel - 1;
|
|
38
|
+
setMaxLevel(nestedLevel);
|
|
39
|
+
var unbind = bind(window, {
|
|
34
40
|
type: 'keydown',
|
|
35
|
-
listener: handleFocus(menuItemRefs.current)
|
|
41
|
+
listener: handleFocus(menuItemRefs.current, nestedLevel, maxLevelRef)
|
|
36
42
|
});
|
|
43
|
+
return function () {
|
|
44
|
+
// Always get the minimun level when multiple levels of menu are closed
|
|
45
|
+
// If the stored level is smaller, we won't update it
|
|
46
|
+
setMaxLevel(prevLevel, true);
|
|
47
|
+
unbind();
|
|
48
|
+
};
|
|
37
49
|
});
|
|
38
50
|
var contextValue = {
|
|
39
51
|
menuItemRefs: menuItemRefs.current,
|
|
@@ -11,7 +11,7 @@ var actionMap = (_actionMap = {}, _defineProperty(_actionMap, KEY_DOWN, 'next'),
|
|
|
11
11
|
*/
|
|
12
12
|
var getNextFocusableElement = function getNextFocusableElement(refs, currentFocusedIdx) {
|
|
13
13
|
while (currentFocusedIdx + 1 < refs.length) {
|
|
14
|
-
var isDisabled = refs[currentFocusedIdx + 1].
|
|
14
|
+
var isDisabled = refs[currentFocusedIdx + 1].hasAttribute('disabled');
|
|
15
15
|
if (!isDisabled) {
|
|
16
16
|
return refs[currentFocusedIdx + 1];
|
|
17
17
|
}
|
|
@@ -27,19 +27,24 @@ var getNextFocusableElement = function getNextFocusableElement(refs, currentFocu
|
|
|
27
27
|
*/
|
|
28
28
|
var getPrevFocusableElement = function getPrevFocusableElement(refs, currentFocusedIdx) {
|
|
29
29
|
while (currentFocusedIdx > 0) {
|
|
30
|
-
var isDisabled = refs[currentFocusedIdx - 1].
|
|
30
|
+
var isDisabled = refs[currentFocusedIdx - 1].hasAttribute('disabled');
|
|
31
31
|
if (!isDisabled) {
|
|
32
32
|
return refs[currentFocusedIdx - 1];
|
|
33
33
|
}
|
|
34
34
|
currentFocusedIdx--;
|
|
35
35
|
}
|
|
36
36
|
};
|
|
37
|
-
export default function handleFocus(refs) {
|
|
37
|
+
export default function handleFocus(refs, nestedLevel, maxLevelRef) {
|
|
38
38
|
return function (e) {
|
|
39
39
|
var currentFocusedIdx = refs.findIndex(function (el) {
|
|
40
40
|
var _document$activeEleme;
|
|
41
41
|
return (_document$activeEleme = document.activeElement) === null || _document$activeEleme === void 0 ? void 0 : _document$activeEleme.isSameNode(el);
|
|
42
42
|
});
|
|
43
|
+
if (nestedLevel < maxLevelRef.current) {
|
|
44
|
+
// if it is a nested dropdown and the level of the given dropdown is not the current level,
|
|
45
|
+
// we don't need to have focus on it
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
43
48
|
var action = actionMap[e.key];
|
|
44
49
|
switch (action) {
|
|
45
50
|
case 'next':
|
|
@@ -47,7 +52,7 @@ export default function handleFocus(refs) {
|
|
|
47
52
|
e.preventDefault();
|
|
48
53
|
if (currentFocusedIdx < refs.length - 1) {
|
|
49
54
|
var _nextFocusableElement = getNextFocusableElement(refs, currentFocusedIdx);
|
|
50
|
-
_nextFocusableElement
|
|
55
|
+
_nextFocusableElement === null || _nextFocusableElement === void 0 ? void 0 : _nextFocusableElement.focus();
|
|
51
56
|
}
|
|
52
57
|
break;
|
|
53
58
|
case 'prev':
|
|
@@ -55,20 +60,20 @@ export default function handleFocus(refs) {
|
|
|
55
60
|
e.preventDefault();
|
|
56
61
|
if (currentFocusedIdx > 0) {
|
|
57
62
|
var _prevFocusableElement = getPrevFocusableElement(refs, currentFocusedIdx);
|
|
58
|
-
_prevFocusableElement
|
|
63
|
+
_prevFocusableElement === null || _prevFocusableElement === void 0 ? void 0 : _prevFocusableElement.focus();
|
|
59
64
|
}
|
|
60
65
|
break;
|
|
61
66
|
case 'first':
|
|
62
67
|
e.preventDefault();
|
|
63
68
|
// Search for first non-disabled element if first element is disabled
|
|
64
69
|
var nextFocusableElement = getNextFocusableElement(refs, -1);
|
|
65
|
-
nextFocusableElement
|
|
70
|
+
nextFocusableElement === null || nextFocusableElement === void 0 ? void 0 : nextFocusableElement.focus();
|
|
66
71
|
break;
|
|
67
72
|
case 'last':
|
|
68
73
|
e.preventDefault();
|
|
69
74
|
// Search for last non-disabled element if last element is disabled
|
|
70
75
|
var prevFocusableElement = getPrevFocusableElement(refs, refs.length);
|
|
71
|
-
prevFocusableElement
|
|
76
|
+
prevFocusableElement === null || prevFocusableElement === void 0 ? void 0 : prevFocusableElement.focus();
|
|
72
77
|
break;
|
|
73
78
|
default:
|
|
74
79
|
return;
|
package/dist/esm/version.json
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React, { FC, MutableRefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
4
|
+
* @internal
|
|
5
|
+
* Context which maintains the current level of dropdown menu if it is nested
|
|
6
|
+
* Default is 0
|
|
7
|
+
*
|
|
8
|
+
*/
|
|
9
|
+
export declare const NestedLevelContext: React.Context<number>;
|
|
10
|
+
/**
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
* Context which maintains the maximun level of dropdown menu if it is nested
|
|
14
|
+
* Default is 0
|
|
15
|
+
*
|
|
16
|
+
*/
|
|
17
|
+
export declare const TrackMaxLevelContext: React.Context<{
|
|
18
|
+
maxLevelRef: MutableRefObject<number>;
|
|
19
|
+
setMaxLevel: (level: number, isMin?: boolean) => void;
|
|
20
|
+
}>;
|
|
21
|
+
/**
|
|
22
|
+
*
|
|
23
|
+
* @internal
|
|
24
|
+
* Context provider which maintains the maximun level of dropdown menu if it is nested
|
|
25
|
+
*
|
|
26
|
+
*/
|
|
27
|
+
export declare const TrackLevelProvider: FC;
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
+
import { MutableRefObject } from 'react';
|
|
1
2
|
import { FocusableElement } from '../../types';
|
|
2
|
-
export default function handleFocus(refs: Array<FocusableElement>): (e: KeyboardEvent) => void;
|
|
3
|
+
export default function handleFocus(refs: Array<FocusableElement>, nestedLevel: Number, maxLevelRef: MutableRefObject<number>): (e: KeyboardEvent) => void;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React, { FC, MutableRefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
4
|
+
* @internal
|
|
5
|
+
* Context which maintains the current level of dropdown menu if it is nested
|
|
6
|
+
* Default is 0
|
|
7
|
+
*
|
|
8
|
+
*/
|
|
9
|
+
export declare const NestedLevelContext: React.Context<number>;
|
|
10
|
+
/**
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
* Context which maintains the maximun level of dropdown menu if it is nested
|
|
14
|
+
* Default is 0
|
|
15
|
+
*
|
|
16
|
+
*/
|
|
17
|
+
export declare const TrackMaxLevelContext: React.Context<{
|
|
18
|
+
maxLevelRef: MutableRefObject<number>;
|
|
19
|
+
setMaxLevel: (level: number, isMin?: boolean) => void;
|
|
20
|
+
}>;
|
|
21
|
+
/**
|
|
22
|
+
*
|
|
23
|
+
* @internal
|
|
24
|
+
* Context provider which maintains the maximun level of dropdown menu if it is nested
|
|
25
|
+
*
|
|
26
|
+
*/
|
|
27
|
+
export declare const TrackLevelProvider: FC;
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
+
import { MutableRefObject } from 'react';
|
|
1
2
|
import { FocusableElement } from '../../types';
|
|
2
|
-
export default function handleFocus(refs: Array<FocusableElement>): (e: KeyboardEvent) => void;
|
|
3
|
+
export default function handleFocus(refs: Array<FocusableElement>, nestedLevel: Number, maxLevelRef: MutableRefObject<number>): (e: KeyboardEvent) => void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/dropdown-menu",
|
|
3
|
-
"version": "11.11.
|
|
3
|
+
"version": "11.11.5",
|
|
4
4
|
"description": "A dropdown menu displays a list of actions or options to a user.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"@atlaskit/primitives": "^1.0.6",
|
|
42
42
|
"@atlaskit/spinner": "^15.5.0",
|
|
43
43
|
"@atlaskit/theme": "^12.5.0",
|
|
44
|
-
"@atlaskit/tokens": "^1.
|
|
44
|
+
"@atlaskit/tokens": "^1.14.0",
|
|
45
45
|
"@atlaskit/visually-hidden": "^1.2.0",
|
|
46
46
|
"@babel/runtime": "^7.0.0",
|
|
47
47
|
"@emotion/react": "^11.7.1",
|