focus-trap-react 5.0.0 → 7.0.1
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 +23 -1
- package/README.md +12 -7
- package/dist/focus-trap-react.js +90 -22
- package/index.d.ts +1 -1
- package/package.json +47 -40
- package/src/focus-trap-react.js +70 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 7.0.1
|
|
4
|
+
|
|
5
|
+
- Fix: PropTypes definition now supports server-side rendering. #83
|
|
6
|
+
|
|
7
|
+
## 7.0.0
|
|
8
|
+
|
|
9
|
+
- Add: Prop types for `<FocusTrap>`.
|
|
10
|
+
- Update: `focus-trap` dependency from 4.0.2 to [5.1.0](https://github.com/davidtheclark/focus-trap/blob/master/CHANGELOG.md#510) for the latest features/fixes it provides. #71
|
|
11
|
+
- **BREAKING** Update: Only React 16.0+ is supported going forward. #55
|
|
12
|
+
- **BREAKING** Update: All dependencies updated to their latest versions.
|
|
13
|
+
- Fix: `children`'s type is `React.ReactNode`, not `React.ReactElement`. #66
|
|
14
|
+
- Fix: Allow mutable object refs to be used for FocusTrap child. #72
|
|
15
|
+
- Fix: `specifiedFocusTrapOptions.includes(optionName)` statement in `componentDidMount()` was causing an exception because `includes()` is not a function defined on `Object`.
|
|
16
|
+
|
|
17
|
+
## 6.0.0
|
|
18
|
+
|
|
19
|
+
- Update focus-trap to 4.0.2, which includes [a queue of traps](https://github.com/davidtheclark/focus-trap/blob/master/CHANGELOG.md#400), so when a trap is paused because another trap activates, it will be unpaused when that other trap deactivates. If Trap A was automatically paused because Trap B activated (existing behavior), when Trap B is deactivated Trap A will be automatically unpaused (new behavior).
|
|
20
|
+
|
|
21
|
+
## 5.0.1
|
|
22
|
+
|
|
23
|
+
- Fix TypeScript declarations.
|
|
24
|
+
|
|
3
25
|
## 5.0.0
|
|
4
26
|
|
|
5
|
-
- **
|
|
27
|
+
- **BREAKING:** `<FocusTrap>` now expects exactly one child element which can be any HTML element or other React component that contains focusable elements. The `tag` prop has been removed, as has support for additional props that are passed through to the `tag`, because it is no longer necessary: you should provide your own element, with whatever props you want, as a child of `<FocusTrap>`.
|
|
6
28
|
|
|
7
29
|
## 4.0.1
|
|
8
30
|
|
package/README.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# focus-trap-react [](https://travis-ci.org/davidtheclark/focus-trap-react)
|
|
2
2
|
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
**SEEKING CO-MAINTAINERS!** Continued development of this project is going to require the work of one or more dedicated co-maintainers (or forkers). If you're interested, please comment in [this issue](https://github.com/davidtheclark/focus-trap-react/issues/48).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
|
|
3
10
|
A React component that traps focus.
|
|
4
11
|
|
|
5
12
|
This component is a light wrapper around [focus-trap](https://github.com/davidtheclark/focus-trap),
|
|
@@ -7,12 +14,6 @@ tailored to your React-specific needs.
|
|
|
7
14
|
|
|
8
15
|
You might want it for, say, building [an accessible modal](https://github.com/davidtheclark/react-aria-modal)?
|
|
9
16
|
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
**Looking for co-maintainers!** If you'd like to help maintain this project, please let me know.
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
17
|
## What it does
|
|
17
18
|
|
|
18
19
|
[Check out the demo](http://davidtheclark.github.io/focus-trap-react/demo/).
|
|
@@ -167,8 +168,12 @@ If you would like to pause or unpause the focus trap (see [`focus-trap`'s docume
|
|
|
167
168
|
|
|
168
169
|
Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.
|
|
169
170
|
|
|
171
|
+
Format with `npm run format`.
|
|
172
|
+
|
|
170
173
|
Lint with `npm run lint`.
|
|
171
174
|
|
|
172
175
|
Run the demos with `npm start`.
|
|
173
176
|
|
|
174
|
-
Test with `npm run test
|
|
177
|
+
Test with `npm run test` (checks formatting, checks lint, runs tests).
|
|
178
|
+
|
|
179
|
+
Unit test (only) with `npm run test-unit`.
|
package/dist/focus-trap-react.js
CHANGED
|
@@ -1,37 +1,67 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
|
|
4
4
|
|
|
5
5
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
6
6
|
|
|
7
|
-
function
|
|
7
|
+
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
|
|
8
8
|
|
|
9
|
-
function
|
|
9
|
+
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
|
|
10
|
+
|
|
11
|
+
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
|
|
12
|
+
|
|
13
|
+
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
|
|
14
|
+
|
|
15
|
+
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
|
|
16
|
+
|
|
17
|
+
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
|
|
18
|
+
|
|
19
|
+
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
|
|
20
|
+
|
|
21
|
+
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
|
|
22
|
+
|
|
23
|
+
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
|
|
24
|
+
|
|
25
|
+
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
10
26
|
|
|
11
27
|
var React = require('react');
|
|
28
|
+
|
|
12
29
|
var ReactDOM = require('react-dom');
|
|
13
|
-
var createFocusTrap = require('focus-trap');
|
|
14
30
|
|
|
15
|
-
var
|
|
31
|
+
var PropTypes = require('prop-types');
|
|
32
|
+
|
|
33
|
+
var createFocusTrap = require('focus-trap'); // TODO: These issues are related to older React features which we'll likely need
|
|
34
|
+
// to fix in order to move the code forward to the next major version of React.
|
|
35
|
+
// @see https://github.com/davidtheclark/focus-trap-react/issues/77
|
|
36
|
+
|
|
37
|
+
/* eslint-disable react/no-find-dom-node */
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
var FocusTrap = /*#__PURE__*/function (_React$Component) {
|
|
16
41
|
_inherits(FocusTrap, _React$Component);
|
|
17
42
|
|
|
43
|
+
var _super = _createSuper(FocusTrap);
|
|
44
|
+
|
|
18
45
|
function FocusTrap(props) {
|
|
46
|
+
var _this;
|
|
47
|
+
|
|
19
48
|
_classCallCheck(this, FocusTrap);
|
|
20
49
|
|
|
21
|
-
|
|
50
|
+
_this = _super.call(this, props);
|
|
22
51
|
|
|
23
|
-
_this
|
|
52
|
+
_defineProperty(_assertThisInitialized(_this), "setFocusTrapElement", function (element) {
|
|
24
53
|
_this.focusTrapElement = element;
|
|
25
|
-
};
|
|
54
|
+
});
|
|
26
55
|
|
|
27
56
|
if (typeof document !== 'undefined') {
|
|
28
57
|
_this.previouslyFocusedElement = document.activeElement;
|
|
29
58
|
}
|
|
59
|
+
|
|
30
60
|
return _this;
|
|
31
61
|
}
|
|
32
62
|
|
|
33
63
|
_createClass(FocusTrap, [{
|
|
34
|
-
key:
|
|
64
|
+
key: "componentDidMount",
|
|
35
65
|
value: function componentDidMount() {
|
|
36
66
|
// We need to hijack the returnFocusOnDeactivate option,
|
|
37
67
|
// because React can move focus into the element before we arrived at
|
|
@@ -42,30 +72,40 @@ var FocusTrap = function (_React$Component) {
|
|
|
42
72
|
var tailoredFocusTrapOptions = {
|
|
43
73
|
returnFocusOnDeactivate: false
|
|
44
74
|
};
|
|
75
|
+
|
|
45
76
|
for (var optionName in specifiedFocusTrapOptions) {
|
|
46
|
-
if (!
|
|
47
|
-
|
|
77
|
+
if (!Object.prototype.hasOwnProperty.call(specifiedFocusTrapOptions, optionName)) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (optionName === 'returnFocusOnDeactivate') {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
|
|
48
85
|
tailoredFocusTrapOptions[optionName] = specifiedFocusTrapOptions[optionName];
|
|
49
86
|
}
|
|
50
87
|
|
|
51
|
-
var focusTrapElementDOMNode = ReactDOM.findDOMNode(this.focusTrapElement);
|
|
88
|
+
var focusTrapElementDOMNode = ReactDOM.findDOMNode(this.focusTrapElement); // eslint-disable-next-line react/prop-types -- _createFocusTrap is an internal prop
|
|
52
89
|
|
|
53
90
|
this.focusTrap = this.props._createFocusTrap(focusTrapElementDOMNode, tailoredFocusTrapOptions);
|
|
91
|
+
|
|
54
92
|
if (this.props.active) {
|
|
55
93
|
this.focusTrap.activate();
|
|
56
94
|
}
|
|
95
|
+
|
|
57
96
|
if (this.props.paused) {
|
|
58
97
|
this.focusTrap.pause();
|
|
59
98
|
}
|
|
60
99
|
}
|
|
61
100
|
}, {
|
|
62
|
-
key:
|
|
101
|
+
key: "componentDidUpdate",
|
|
63
102
|
value: function componentDidUpdate(prevProps) {
|
|
64
103
|
if (prevProps.active && !this.props.active) {
|
|
65
104
|
var returnFocusOnDeactivate = this.props.focusTrapOptions.returnFocusOnDeactivate;
|
|
66
|
-
|
|
67
105
|
var returnFocus = returnFocusOnDeactivate || false;
|
|
68
|
-
var config = {
|
|
106
|
+
var config = {
|
|
107
|
+
returnFocus: returnFocus
|
|
108
|
+
};
|
|
69
109
|
this.focusTrap.deactivate(config);
|
|
70
110
|
} else if (!prevProps.active && this.props.active) {
|
|
71
111
|
this.focusTrap.activate();
|
|
@@ -78,15 +118,16 @@ var FocusTrap = function (_React$Component) {
|
|
|
78
118
|
}
|
|
79
119
|
}
|
|
80
120
|
}, {
|
|
81
|
-
key:
|
|
121
|
+
key: "componentWillUnmount",
|
|
82
122
|
value: function componentWillUnmount() {
|
|
83
123
|
this.focusTrap.deactivate();
|
|
124
|
+
|
|
84
125
|
if (this.props.focusTrapOptions.returnFocusOnDeactivate !== false && this.previouslyFocusedElement && this.previouslyFocusedElement.focus) {
|
|
85
126
|
this.previouslyFocusedElement.focus();
|
|
86
127
|
}
|
|
87
128
|
}
|
|
88
129
|
}, {
|
|
89
|
-
key:
|
|
130
|
+
key: "render",
|
|
90
131
|
value: function render() {
|
|
91
132
|
var _this2 = this;
|
|
92
133
|
|
|
@@ -94,25 +135,52 @@ var FocusTrap = function (_React$Component) {
|
|
|
94
135
|
|
|
95
136
|
var composedRefCallback = function composedRefCallback(element) {
|
|
96
137
|
_this2.setFocusTrapElement(element);
|
|
138
|
+
|
|
97
139
|
if (typeof child.ref === 'function') {
|
|
98
140
|
child.ref(element);
|
|
141
|
+
} else if (child.ref) {
|
|
142
|
+
child.ref.current = element;
|
|
99
143
|
}
|
|
100
144
|
};
|
|
101
145
|
|
|
102
|
-
var childWithRef = React.cloneElement(child, {
|
|
103
|
-
|
|
146
|
+
var childWithRef = React.cloneElement(child, {
|
|
147
|
+
ref: composedRefCallback
|
|
148
|
+
});
|
|
104
149
|
return childWithRef;
|
|
105
150
|
}
|
|
106
151
|
}]);
|
|
107
152
|
|
|
108
153
|
return FocusTrap;
|
|
109
|
-
}(React.Component);
|
|
154
|
+
}(React.Component); // support server-side rendering where `Element` will not be defined
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
var ElementType = typeof Element === 'undefined' ? Function : Element;
|
|
158
|
+
FocusTrap.propTypes = {
|
|
159
|
+
active: PropTypes.bool,
|
|
160
|
+
paused: PropTypes.bool,
|
|
161
|
+
focusTrapOptions: PropTypes.shape({
|
|
162
|
+
onActivate: PropTypes.func,
|
|
163
|
+
onDeactivate: PropTypes.func,
|
|
164
|
+
initialFocus: PropTypes.oneOfType([PropTypes.instanceOf(ElementType), PropTypes.string, PropTypes.func]),
|
|
165
|
+
fallbackFocus: PropTypes.oneOfType([PropTypes.instanceOf(ElementType), PropTypes.string, PropTypes.func]),
|
|
166
|
+
escapeDeactivates: PropTypes.bool,
|
|
167
|
+
clickOutsideDeactivates: PropTypes.bool,
|
|
168
|
+
returnFocusOnDeactivate: PropTypes.bool,
|
|
169
|
+
setReturnFocus: PropTypes.oneOfType([PropTypes.instanceOf(ElementType), PropTypes.string, PropTypes.func]),
|
|
170
|
+
allowOutsideClick: PropTypes.func,
|
|
171
|
+
preventScroll: PropTypes.bool
|
|
172
|
+
}),
|
|
173
|
+
children: PropTypes.oneOfType([PropTypes.element, // React element
|
|
174
|
+
PropTypes.instanceOf(ElementType) // DOM element
|
|
175
|
+
]) // NOTE: _createFocusTrap is internal, for testing purposes only, so we don't
|
|
176
|
+
// specify it here. It's expected to be set to the function returned from
|
|
177
|
+
// require('focus-trap'), or one with a compatible interface.
|
|
110
178
|
|
|
179
|
+
};
|
|
111
180
|
FocusTrap.defaultProps = {
|
|
112
181
|
active: true,
|
|
113
182
|
paused: false,
|
|
114
183
|
focusTrapOptions: {},
|
|
115
184
|
_createFocusTrap: createFocusTrap
|
|
116
185
|
};
|
|
117
|
-
|
|
118
186
|
module.exports = FocusTrap;
|
package/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "focus-trap-react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.1",
|
|
4
4
|
"description": "A React component that traps focus.",
|
|
5
5
|
"main": "dist/focus-trap-react.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"demo-bundle": "browserify demo/js -t babelify --extension=.jsx -o demo/demo-bundle.js",
|
|
9
|
+
"prestart": "yarn build",
|
|
9
10
|
"start": "budo demo/js/index.js:demo-bundle.js --dir demo --live -- -t babelify --extension=.jsx",
|
|
10
|
-
"lint": "eslint
|
|
11
|
-
"format": "prettier --
|
|
11
|
+
"lint": "eslint \"src/**/*.js\" \"test/**/*.js\" \"demo/**/*.js\"",
|
|
12
|
+
"format": "prettier --write \"src/**/*.js\" \"test/**/*.js\" \"demo/js/**/*.js\"",
|
|
13
|
+
"format-check": "prettier --check \"src/**/*.js\" \"test/**/*.js\" \"demo/js/**/*.js\"",
|
|
12
14
|
"build": "babel src -d dist",
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"prepublishOnly": "npm run build"
|
|
15
|
+
"test-unit": "jest",
|
|
16
|
+
"test": "yarn format-check && yarn lint && jest",
|
|
17
|
+
"prepublishOnly": "yarn test && yarn build"
|
|
17
18
|
},
|
|
18
19
|
"repository": {
|
|
19
20
|
"type": "git",
|
|
20
21
|
"url": "git+https://github.com/davidtheclark/focus-trap-react.git"
|
|
21
22
|
},
|
|
22
23
|
"files": [
|
|
24
|
+
"README.md",
|
|
25
|
+
"LICENSE",
|
|
26
|
+
"CHANGELOG.md",
|
|
23
27
|
"dist",
|
|
24
28
|
"src",
|
|
25
29
|
"index.d.ts"
|
|
@@ -36,48 +40,51 @@
|
|
|
36
40
|
"keyboard"
|
|
37
41
|
],
|
|
38
42
|
"author": "David Clark",
|
|
43
|
+
"contributors": [
|
|
44
|
+
{
|
|
45
|
+
"name": "David Clark",
|
|
46
|
+
"url": "http://davidtheclark.com/"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "Stefan Cameron",
|
|
50
|
+
"url": "https://stefancameron.com/"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "Marais Rossouw",
|
|
54
|
+
"url": "https://marais.io/"
|
|
55
|
+
}
|
|
56
|
+
],
|
|
39
57
|
"license": "MIT",
|
|
40
58
|
"bugs": {
|
|
41
59
|
"url": "https://github.com/davidtheclark/focus-trap-react/issues"
|
|
42
60
|
},
|
|
43
61
|
"homepage": "https://github.com/davidtheclark/focus-trap-react#readme",
|
|
44
62
|
"devDependencies": {
|
|
45
|
-
"babel
|
|
46
|
-
"babel
|
|
47
|
-
"babel
|
|
48
|
-
"babel
|
|
49
|
-
"babel
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"prettier": "^
|
|
57
|
-
"react": "^
|
|
58
|
-
"
|
|
63
|
+
"@babel/cli": "^7.10.3",
|
|
64
|
+
"@babel/core": "^7.10.3",
|
|
65
|
+
"@babel/plugin-proposal-class-properties": "^7.10.1",
|
|
66
|
+
"@babel/preset-env": "^7.10.2",
|
|
67
|
+
"@babel/preset-react": "^7.10.1",
|
|
68
|
+
"babel-eslint": "^10.1.0",
|
|
69
|
+
"babel-jest": "^26.0.1",
|
|
70
|
+
"babelify": "^10.0.0",
|
|
71
|
+
"browserify": "^16.5.1",
|
|
72
|
+
"budo": "^11.6.3",
|
|
73
|
+
"eslint": "^7.3.0",
|
|
74
|
+
"eslint-config-prettier": "^6.11.0",
|
|
75
|
+
"eslint-plugin-react": "^7.20.0",
|
|
76
|
+
"jest": "^26.0.1",
|
|
77
|
+
"prettier": "^2.0.5",
|
|
78
|
+
"prop-types": "^15.7.2",
|
|
79
|
+
"react": "^16.13.1",
|
|
80
|
+
"react-dom": "^16.13.1"
|
|
59
81
|
},
|
|
60
82
|
"dependencies": {
|
|
61
|
-
"focus-trap": "^
|
|
83
|
+
"focus-trap": "^5.1.0"
|
|
62
84
|
},
|
|
63
85
|
"peerDependencies": {
|
|
64
|
-
"react": "
|
|
65
|
-
"react-dom": "
|
|
66
|
-
|
|
67
|
-
"babel": {
|
|
68
|
-
"presets": [
|
|
69
|
-
"react",
|
|
70
|
-
"es2015"
|
|
71
|
-
],
|
|
72
|
-
"plugins": [
|
|
73
|
-
"transform-class-properties"
|
|
74
|
-
]
|
|
75
|
-
},
|
|
76
|
-
"jest": {
|
|
77
|
-
"setupFiles": [
|
|
78
|
-
"<rootDir>/test/jest-setup.js"
|
|
79
|
-
],
|
|
80
|
-
"clearMocks": true,
|
|
81
|
-
"testURL": "http://localhost"
|
|
86
|
+
"react": "^16.0.0",
|
|
87
|
+
"react-dom": "^16.0.0",
|
|
88
|
+
"prop-types": "^15.7.2"
|
|
82
89
|
}
|
|
83
90
|
}
|
package/src/focus-trap-react.js
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
const React = require('react');
|
|
2
2
|
const ReactDOM = require('react-dom');
|
|
3
|
+
const PropTypes = require('prop-types');
|
|
3
4
|
const createFocusTrap = require('focus-trap');
|
|
4
5
|
|
|
6
|
+
// TODO: These issues are related to older React features which we'll likely need
|
|
7
|
+
// to fix in order to move the code forward to the next major version of React.
|
|
8
|
+
// @see https://github.com/davidtheclark/focus-trap-react/issues/77
|
|
9
|
+
/* eslint-disable react/no-find-dom-node */
|
|
10
|
+
|
|
5
11
|
class FocusTrap extends React.Component {
|
|
6
12
|
constructor(props) {
|
|
7
|
-
super(props)
|
|
13
|
+
super(props);
|
|
8
14
|
|
|
9
15
|
if (typeof document !== 'undefined') {
|
|
10
16
|
this.previouslyFocusedElement = document.activeElement;
|
|
@@ -21,15 +27,28 @@ class FocusTrap extends React.Component {
|
|
|
21
27
|
const tailoredFocusTrapOptions = {
|
|
22
28
|
returnFocusOnDeactivate: false
|
|
23
29
|
};
|
|
30
|
+
|
|
24
31
|
for (const optionName in specifiedFocusTrapOptions) {
|
|
25
|
-
if (
|
|
26
|
-
|
|
32
|
+
if (
|
|
33
|
+
!Object.prototype.hasOwnProperty.call(
|
|
34
|
+
specifiedFocusTrapOptions,
|
|
35
|
+
optionName
|
|
36
|
+
)
|
|
37
|
+
) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (optionName === 'returnFocusOnDeactivate') {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
|
|
27
45
|
tailoredFocusTrapOptions[optionName] =
|
|
28
46
|
specifiedFocusTrapOptions[optionName];
|
|
29
47
|
}
|
|
30
48
|
|
|
31
49
|
const focusTrapElementDOMNode = ReactDOM.findDOMNode(this.focusTrapElement);
|
|
32
50
|
|
|
51
|
+
// eslint-disable-next-line react/prop-types -- _createFocusTrap is an internal prop
|
|
33
52
|
this.focusTrap = this.props._createFocusTrap(
|
|
34
53
|
focusTrapElementDOMNode,
|
|
35
54
|
tailoredFocusTrapOptions
|
|
@@ -70,26 +89,70 @@ class FocusTrap extends React.Component {
|
|
|
70
89
|
}
|
|
71
90
|
}
|
|
72
91
|
|
|
73
|
-
setFocusTrapElement = element => {
|
|
92
|
+
setFocusTrapElement = (element) => {
|
|
74
93
|
this.focusTrapElement = element;
|
|
75
94
|
};
|
|
76
95
|
|
|
77
96
|
render() {
|
|
78
97
|
const child = React.Children.only(this.props.children);
|
|
79
98
|
|
|
80
|
-
const composedRefCallback = element => {
|
|
99
|
+
const composedRefCallback = (element) => {
|
|
81
100
|
this.setFocusTrapElement(element);
|
|
82
101
|
if (typeof child.ref === 'function') {
|
|
83
102
|
child.ref(element);
|
|
103
|
+
} else if (child.ref) {
|
|
104
|
+
child.ref.current = element;
|
|
84
105
|
}
|
|
85
|
-
}
|
|
106
|
+
};
|
|
86
107
|
|
|
87
|
-
const childWithRef = React.cloneElement(child, {
|
|
108
|
+
const childWithRef = React.cloneElement(child, {
|
|
109
|
+
ref: composedRefCallback
|
|
110
|
+
});
|
|
88
111
|
|
|
89
112
|
return childWithRef;
|
|
90
113
|
}
|
|
91
114
|
}
|
|
92
115
|
|
|
116
|
+
// support server-side rendering where `Element` will not be defined
|
|
117
|
+
const ElementType = typeof Element === 'undefined' ? Function : Element;
|
|
118
|
+
|
|
119
|
+
FocusTrap.propTypes = {
|
|
120
|
+
active: PropTypes.bool,
|
|
121
|
+
paused: PropTypes.bool,
|
|
122
|
+
focusTrapOptions: PropTypes.shape({
|
|
123
|
+
onActivate: PropTypes.func,
|
|
124
|
+
onDeactivate: PropTypes.func,
|
|
125
|
+
initialFocus: PropTypes.oneOfType([
|
|
126
|
+
PropTypes.instanceOf(ElementType),
|
|
127
|
+
PropTypes.string,
|
|
128
|
+
PropTypes.func
|
|
129
|
+
]),
|
|
130
|
+
fallbackFocus: PropTypes.oneOfType([
|
|
131
|
+
PropTypes.instanceOf(ElementType),
|
|
132
|
+
PropTypes.string,
|
|
133
|
+
PropTypes.func
|
|
134
|
+
]),
|
|
135
|
+
escapeDeactivates: PropTypes.bool,
|
|
136
|
+
clickOutsideDeactivates: PropTypes.bool,
|
|
137
|
+
returnFocusOnDeactivate: PropTypes.bool,
|
|
138
|
+
setReturnFocus: PropTypes.oneOfType([
|
|
139
|
+
PropTypes.instanceOf(ElementType),
|
|
140
|
+
PropTypes.string,
|
|
141
|
+
PropTypes.func
|
|
142
|
+
]),
|
|
143
|
+
allowOutsideClick: PropTypes.func,
|
|
144
|
+
preventScroll: PropTypes.bool
|
|
145
|
+
}),
|
|
146
|
+
children: PropTypes.oneOfType([
|
|
147
|
+
PropTypes.element, // React element
|
|
148
|
+
PropTypes.instanceOf(ElementType) // DOM element
|
|
149
|
+
])
|
|
150
|
+
|
|
151
|
+
// NOTE: _createFocusTrap is internal, for testing purposes only, so we don't
|
|
152
|
+
// specify it here. It's expected to be set to the function returned from
|
|
153
|
+
// require('focus-trap'), or one with a compatible interface.
|
|
154
|
+
};
|
|
155
|
+
|
|
93
156
|
FocusTrap.defaultProps = {
|
|
94
157
|
active: true,
|
|
95
158
|
paused: false,
|