@skyscanner/backpack-web 3.1.0 → 4.0.0
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/bpk-animate-height/package.json +2 -2
- package/bpk-component-accordion/package.json +6 -6
- package/bpk-component-aria-live/package.json +7 -7
- package/bpk-component-autosuggest/package.json +4 -4
- package/bpk-component-badge/package.json +4 -4
- package/bpk-component-banner-alert/package.json +8 -8
- package/bpk-component-barchart/package.json +7 -7
- package/bpk-component-blockquote/package.json +2 -2
- package/bpk-component-breadcrumb/package.json +5 -5
- package/bpk-component-breakpoint/package.json +2 -2
- package/bpk-component-button/package.json +2 -2
- package/bpk-component-calendar/package.json +8 -8
- package/bpk-component-card/package.json +4 -4
- package/bpk-component-checkbox/package.json +4 -4
- package/bpk-component-chip/package.json +5 -5
- package/bpk-component-close-button/package.json +4 -4
- package/bpk-component-code/package.json +2 -2
- package/bpk-component-content-container/package.json +2 -2
- package/bpk-component-datatable/package.json +3 -3
- package/bpk-component-datepicker/package.json +8 -8
- package/bpk-component-description-list/package.json +2 -2
- package/bpk-component-dialog/package.json +8 -8
- package/bpk-component-drawer/package.json +8 -8
- package/bpk-component-fieldset/package.json +12 -12
- package/bpk-component-flare/package.json +4 -4
- package/bpk-component-floating-notification/README.md +53 -0
- package/bpk-component-floating-notification/index.js +27 -0
- package/bpk-component-floating-notification/package.json +25 -0
- package/bpk-component-floating-notification/src/BpkFloatingNotification.js +150 -0
- package/bpk-component-floating-notification/src/BpkFloatingNotification.module.css +18 -0
- package/bpk-component-floating-notification/src/BpkFloatingNotification.module.scss +96 -0
- package/bpk-component-form-validation/package.json +9 -9
- package/bpk-component-graphic-promotion/package.json +4 -5
- package/bpk-component-graphic-promotion/src/BpkGraphicPromo.js +22 -6
- package/bpk-component-graphic-promotion/src/BpkGraphicPromo.module.scss +14 -4
- package/bpk-component-grid/package.json +2 -2
- package/bpk-component-grid-toggle/package.json +3 -3
- package/bpk-component-horizontal-nav/package.json +5 -5
- package/bpk-component-icon/package.json +5 -5
- package/bpk-component-image/package.json +6 -6
- package/bpk-component-infinite-scroll/package.json +6 -6
- package/bpk-component-input/package.json +6 -6
- package/bpk-component-label/package.json +2 -2
- package/bpk-component-link/package.json +3 -3
- package/bpk-component-list/package.json +2 -2
- package/bpk-component-loading-button/package.json +6 -6
- package/bpk-component-map/package.json +6 -6
- package/bpk-component-mobile-scroll-container/package.json +2 -2
- package/bpk-component-modal/package.json +9 -9
- package/bpk-component-navigation-bar/package.json +7 -7
- package/bpk-component-navigation-stack/package.json +6 -6
- package/bpk-component-nudger/package.json +6 -6
- package/bpk-component-overlay/package.json +4 -4
- package/bpk-component-pagination/package.json +4 -4
- package/bpk-component-panel/package.json +2 -2
- package/bpk-component-phone-input/package.json +8 -8
- package/bpk-component-popover/package.json +8 -8
- package/bpk-component-popover/src/BpkPopoverPortal.js +3 -3
- package/bpk-component-price/package.json +3 -3
- package/bpk-component-progress/package.json +4 -4
- package/bpk-component-radio/package.json +3 -3
- package/bpk-component-rating/package.json +3 -3
- package/bpk-component-rtl-toggle/package.json +3 -3
- package/bpk-component-scrollable-calendar/package.json +5 -5
- package/bpk-component-section-list/package.json +5 -5
- package/bpk-component-select/package.json +3 -3
- package/bpk-component-skip-link/package.json +2 -2
- package/bpk-component-slider/package.json +3 -3
- package/bpk-component-spinner/package.json +2 -2
- package/bpk-component-star-rating/package.json +5 -5
- package/bpk-component-switch/package.json +2 -2
- package/bpk-component-table/package.json +2 -2
- package/bpk-component-text/package.json +2 -2
- package/bpk-component-textarea/package.json +4 -4
- package/bpk-component-theme-toggle/package.json +4 -4
- package/bpk-component-ticket/package.json +4 -4
- package/bpk-component-tooltip/package.json +3 -3
- package/bpk-component-tooltip/src/BpkTooltipPortal.js +3 -3
- package/bpk-react-utils/index.js +3 -0
- package/bpk-react-utils/package.json +1 -1
- package/bpk-react-utils/src/Portal-v1.js +310 -0
- package/bpk-react-utils/src/Portal.js +42 -54
- package/bpk-scrim-utils/package.json +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Backpack - Skyscanner's Design System
|
|
3
|
+
*
|
|
4
|
+
* Copyright 2016-2021 Skyscanner Ltd
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
unstable_renderSubtreeIntoContainer, // eslint-disable-line camelcase
|
|
21
|
+
unmountComponentAtNode,
|
|
22
|
+
findDOMNode,
|
|
23
|
+
} from 'react-dom';
|
|
24
|
+
import { Component } from 'react';
|
|
25
|
+
import assign from 'object-assign';
|
|
26
|
+
import PropTypes from 'prop-types';
|
|
27
|
+
|
|
28
|
+
const KEYCODES = {
|
|
29
|
+
ESCAPE: 'Escape',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
class PortalV1 extends Component {
|
|
33
|
+
constructor() {
|
|
34
|
+
super();
|
|
35
|
+
|
|
36
|
+
this.portalElement = null;
|
|
37
|
+
|
|
38
|
+
// shouldClose is used to keep track of the user's mouse-down events in order to
|
|
39
|
+
// prevent the dialog closing if the mouse leaves / enters the portal during the click
|
|
40
|
+
this.shouldClose = false;
|
|
41
|
+
|
|
42
|
+
this.close = this.close.bind(this);
|
|
43
|
+
this.onDocumentMouseUp = this.onDocumentMouseUp.bind(this);
|
|
44
|
+
this.onDocumentMouseDown = this.onDocumentMouseDown.bind(this);
|
|
45
|
+
this.onDocumentKeyDown = this.onDocumentKeyDown.bind(this);
|
|
46
|
+
this.onDocumentMouseMove = this.onDocumentMouseMove.bind(this);
|
|
47
|
+
this.getClickEventProperties = this.getClickEventProperties.bind(this);
|
|
48
|
+
this.supportsPassiveEvents = this.supportsPassiveEvents.bind(this);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
componentDidMount() {
|
|
52
|
+
if (this.props.isOpen) {
|
|
53
|
+
this.open();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (this.props.targetRef) {
|
|
57
|
+
this.props.targetRef(this.getTargetElement());
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// When the consumer updates `isOpen` to be `false`, we
|
|
62
|
+
// call their beforeClose function so that they can trigger the close.
|
|
63
|
+
// If they don't provide `beforeClose` we just call `close` directly.
|
|
64
|
+
componentDidUpdate(prevProps) {
|
|
65
|
+
if (this.props.isOpen) {
|
|
66
|
+
if (!prevProps.isOpen) {
|
|
67
|
+
this.open();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
} else if (prevProps.isOpen) {
|
|
71
|
+
if (this.props.beforeClose) {
|
|
72
|
+
this.props.beforeClose(this.close);
|
|
73
|
+
} else {
|
|
74
|
+
this.close();
|
|
75
|
+
}
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
this.renderPortal();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
componentWillUnmount() {
|
|
83
|
+
this.close();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
onDocumentMouseDown(event) {
|
|
87
|
+
const clickEventProperties = this.getClickEventProperties(event);
|
|
88
|
+
if (
|
|
89
|
+
clickEventProperties.isNotLeftClick ||
|
|
90
|
+
clickEventProperties.isTargetClick ||
|
|
91
|
+
clickEventProperties.isPortalClick
|
|
92
|
+
) {
|
|
93
|
+
this.shouldClose = false;
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
this.shouldClose = true;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
onDocumentMouseUp(event) {
|
|
101
|
+
const clickEventProperties = this.getClickEventProperties(event);
|
|
102
|
+
|
|
103
|
+
if (
|
|
104
|
+
clickEventProperties.isNotLeftClick ||
|
|
105
|
+
clickEventProperties.isTargetClick ||
|
|
106
|
+
clickEventProperties.isPortalClick
|
|
107
|
+
) {
|
|
108
|
+
this.shouldClose = false;
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (this.shouldClose) {
|
|
113
|
+
// `onClose` tells the consumer that they should change `isOpen` to false.
|
|
114
|
+
// Once the consumer has responded to `onClose`, `beforeClose` and `close` will be called.
|
|
115
|
+
this.props.onClose(event, { source: 'DOCUMENT_CLICK' });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
onDocumentKeyDown(event) {
|
|
120
|
+
if (
|
|
121
|
+
event.key === KEYCODES.ESCAPE &&
|
|
122
|
+
this.props.isOpen &&
|
|
123
|
+
this.props.closeOnEscPressed
|
|
124
|
+
) {
|
|
125
|
+
// `onClose` tells the consumer that they should change `isOpen` to false.
|
|
126
|
+
// Once the consumer has responded to `onClose`, `beforeClose` and `close` will be called.
|
|
127
|
+
this.props.onClose(event, { source: 'ESCAPE' });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
onDocumentMouseMove() {
|
|
132
|
+
this.shouldClose = false;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
getClickEventProperties(event) {
|
|
136
|
+
const isNotLeftClick = event.button && event.button !== 0;
|
|
137
|
+
|
|
138
|
+
const targetElement = this.getTargetElement();
|
|
139
|
+
const isTargetClick =
|
|
140
|
+
targetElement &&
|
|
141
|
+
(event.target === targetElement || targetElement.contains(event.target));
|
|
142
|
+
|
|
143
|
+
const isPortalClick =
|
|
144
|
+
this.portalElement &&
|
|
145
|
+
(event.target === this.portalElement ||
|
|
146
|
+
this.portalElement.contains(event.target));
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
isNotLeftClick,
|
|
150
|
+
isTargetClick,
|
|
151
|
+
isPortalClick,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
getTargetElement() {
|
|
156
|
+
if (typeof this.props.target === 'function') {
|
|
157
|
+
return this.props.target();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Whilst findDOMNode is planned for deprecation in a future implementation of react, since this is the only usage
|
|
161
|
+
// in backpack, we have decided to ignore this instance as it'll be deleted in favour of React 16 first class portal
|
|
162
|
+
// implementation (see https://reactjs.org/docs/portals.html).
|
|
163
|
+
// eslint-disable-next-line react/no-find-dom-node
|
|
164
|
+
return this.props.target && findDOMNode(this);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
getRenderTarget() {
|
|
168
|
+
if (typeof this.props.renderTarget === 'function') {
|
|
169
|
+
return this.props.renderTarget();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return document.body;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
open() {
|
|
176
|
+
if (this.portalElement) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
this.portalElement = document.createElement('div');
|
|
181
|
+
this.getRenderTarget().appendChild(this.portalElement);
|
|
182
|
+
|
|
183
|
+
const passiveArgs = this.supportsPassiveEvents()
|
|
184
|
+
? { passive: true }
|
|
185
|
+
: false;
|
|
186
|
+
document.addEventListener(
|
|
187
|
+
'touchstart',
|
|
188
|
+
this.onDocumentMouseDown,
|
|
189
|
+
passiveArgs,
|
|
190
|
+
);
|
|
191
|
+
document.addEventListener(
|
|
192
|
+
'touchmove',
|
|
193
|
+
this.onDocumentMouseMove,
|
|
194
|
+
passiveArgs,
|
|
195
|
+
);
|
|
196
|
+
document.addEventListener('touchend', this.onDocumentMouseUp, passiveArgs);
|
|
197
|
+
document.addEventListener('mousedown', this.onDocumentMouseDown, false);
|
|
198
|
+
document.addEventListener('mouseup', this.onDocumentMouseUp, false);
|
|
199
|
+
document.addEventListener('keydown', this.onDocumentKeyDown, false);
|
|
200
|
+
|
|
201
|
+
if (this.props.style) {
|
|
202
|
+
assign(this.portalElement.style, this.props.style);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (this.props.className) {
|
|
206
|
+
this.portalElement.className = this.props.className;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
this.renderPortal(() => {
|
|
210
|
+
this.props.onOpen(this.portalElement, this.getTargetElement());
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
close() {
|
|
215
|
+
if (!this.portalElement) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
unmountComponentAtNode(this.portalElement);
|
|
220
|
+
|
|
221
|
+
const renderTarget = this.getRenderTarget();
|
|
222
|
+
if (renderTarget) {
|
|
223
|
+
renderTarget.removeChild(this.portalElement);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
document.removeEventListener('touchstart', this.onDocumentMouseDown);
|
|
227
|
+
document.removeEventListener('touchmove', this.onDocumentMouseMove);
|
|
228
|
+
document.removeEventListener('touchend', this.onDocumentMouseUp);
|
|
229
|
+
document.removeEventListener('mousedown', this.onDocumentMouseDown);
|
|
230
|
+
document.removeEventListener('mouseup', this.onDocumentMouseUp);
|
|
231
|
+
document.removeEventListener('keydown', this.onDocumentKeyDown);
|
|
232
|
+
|
|
233
|
+
this.portalElement = null;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// This function is taken from modernizr
|
|
237
|
+
// See https://github.com/modernizr/modernizr
|
|
238
|
+
// eslint-disable-next-line class-methods-use-this
|
|
239
|
+
supportsPassiveEvents() {
|
|
240
|
+
let supportsPassiveOption = false;
|
|
241
|
+
try {
|
|
242
|
+
const opts = Object.defineProperty({}, 'passive', {
|
|
243
|
+
// eslint-disable-next-line getter-return
|
|
244
|
+
get() {
|
|
245
|
+
supportsPassiveOption = true;
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
window.addEventListener('test', null, opts);
|
|
249
|
+
} catch (e) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
return supportsPassiveOption;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
renderPortal(cb = () => {}) {
|
|
256
|
+
// If the `target` prop is null, it's fine that there is no targetElement
|
|
257
|
+
// Otherwise, if a `target` is provided, we don't render if we cannot find the respective element
|
|
258
|
+
const missesExpectedTarget = this.props.target && !this.getTargetElement();
|
|
259
|
+
|
|
260
|
+
if (this.portalElement && !missesExpectedTarget) {
|
|
261
|
+
unstable_renderSubtreeIntoContainer(
|
|
262
|
+
this,
|
|
263
|
+
this.props.children,
|
|
264
|
+
this.portalElement,
|
|
265
|
+
() => {
|
|
266
|
+
if (this.props.isOpen) {
|
|
267
|
+
this.props.onRender(this.portalElement, this.getTargetElement());
|
|
268
|
+
}
|
|
269
|
+
cb();
|
|
270
|
+
},
|
|
271
|
+
);
|
|
272
|
+
} else {
|
|
273
|
+
setImmediate(cb);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
render() {
|
|
278
|
+
return typeof this.props.target === 'function' ? null : this.props.target;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
PortalV1.propTypes = {
|
|
283
|
+
children: PropTypes.node.isRequired,
|
|
284
|
+
isOpen: PropTypes.bool.isRequired,
|
|
285
|
+
beforeClose: PropTypes.func,
|
|
286
|
+
className: PropTypes.string,
|
|
287
|
+
onClose: PropTypes.func,
|
|
288
|
+
onOpen: PropTypes.func,
|
|
289
|
+
onRender: PropTypes.func,
|
|
290
|
+
style: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
|
291
|
+
renderTarget: PropTypes.func,
|
|
292
|
+
target: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
|
|
293
|
+
targetRef: PropTypes.func,
|
|
294
|
+
closeOnEscPressed: PropTypes.bool,
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
PortalV1.defaultProps = {
|
|
298
|
+
beforeClose: null,
|
|
299
|
+
className: null,
|
|
300
|
+
onClose: () => null,
|
|
301
|
+
onOpen: () => null,
|
|
302
|
+
onRender: () => null,
|
|
303
|
+
style: null,
|
|
304
|
+
renderTarget: null,
|
|
305
|
+
target: null,
|
|
306
|
+
targetRef: null,
|
|
307
|
+
closeOnEscPressed: true,
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
export default PortalV1;
|
|
@@ -16,17 +16,13 @@
|
|
|
16
16
|
* limitations under the License.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import {
|
|
20
|
-
unstable_renderSubtreeIntoContainer, // eslint-disable-line camelcase
|
|
21
|
-
unmountComponentAtNode,
|
|
22
|
-
findDOMNode,
|
|
23
|
-
} from 'react-dom';
|
|
24
19
|
import { Component } from 'react';
|
|
20
|
+
import { createPortal } from 'react-dom';
|
|
25
21
|
import assign from 'object-assign';
|
|
26
22
|
import PropTypes from 'prop-types';
|
|
27
23
|
|
|
28
24
|
const KEYCODES = {
|
|
29
|
-
ESCAPE:
|
|
25
|
+
ESCAPE: 'Escape',
|
|
30
26
|
};
|
|
31
27
|
|
|
32
28
|
class Portal extends Component {
|
|
@@ -35,6 +31,10 @@ class Portal extends Component {
|
|
|
35
31
|
|
|
36
32
|
this.portalElement = null;
|
|
37
33
|
|
|
34
|
+
this.state = {
|
|
35
|
+
isVisible: false,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
38
|
// shouldClose is used to keep track of the user's mouse-down events in order to
|
|
39
39
|
// prevent the dialog closing if the mouse leaves / enters the portal during the click
|
|
40
40
|
this.shouldClose = false;
|
|
@@ -65,7 +65,8 @@ class Portal extends Component {
|
|
|
65
65
|
if (this.props.isOpen) {
|
|
66
66
|
if (!prevProps.isOpen) {
|
|
67
67
|
this.open();
|
|
68
|
-
|
|
68
|
+
} else {
|
|
69
|
+
this.props.onRender(this.portalElement, this.getTargetElement());
|
|
69
70
|
}
|
|
70
71
|
} else if (prevProps.isOpen) {
|
|
71
72
|
if (this.props.beforeClose) {
|
|
@@ -73,10 +74,7 @@ class Portal extends Component {
|
|
|
73
74
|
} else {
|
|
74
75
|
this.close();
|
|
75
76
|
}
|
|
76
|
-
return;
|
|
77
77
|
}
|
|
78
|
-
|
|
79
|
-
this.renderPortal();
|
|
80
78
|
}
|
|
81
79
|
|
|
82
80
|
componentWillUnmount() {
|
|
@@ -118,7 +116,7 @@ class Portal extends Component {
|
|
|
118
116
|
|
|
119
117
|
onDocumentKeyDown(event) {
|
|
120
118
|
if (
|
|
121
|
-
event.
|
|
119
|
+
event.key === KEYCODES.ESCAPE &&
|
|
122
120
|
this.props.isOpen &&
|
|
123
121
|
this.props.closeOnEscPressed
|
|
124
122
|
) {
|
|
@@ -138,38 +136,42 @@ class Portal extends Component {
|
|
|
138
136
|
const targetElement = this.getTargetElement();
|
|
139
137
|
const isTargetClick =
|
|
140
138
|
targetElement &&
|
|
139
|
+
event.target instanceof Node &&
|
|
141
140
|
(event.target === targetElement || targetElement.contains(event.target));
|
|
142
141
|
|
|
143
142
|
const isPortalClick =
|
|
144
143
|
this.portalElement &&
|
|
144
|
+
event.target instanceof Node &&
|
|
145
145
|
(event.target === this.portalElement ||
|
|
146
146
|
this.portalElement.contains(event.target));
|
|
147
147
|
|
|
148
148
|
return {
|
|
149
|
-
isNotLeftClick,
|
|
150
|
-
isTargetClick,
|
|
151
|
-
isPortalClick,
|
|
149
|
+
isNotLeftClick: !!isNotLeftClick,
|
|
150
|
+
isTargetClick: !!isTargetClick,
|
|
151
|
+
isPortalClick: !!isPortalClick,
|
|
152
152
|
};
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
getTargetElement() {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
// Whilst findDOMNode is planned for deprecation in a future implementation of react, since this is the only usage
|
|
161
|
-
// in backpack, we have decided to ignore this instance as it'll be deleted in favour of React 16 first class portal
|
|
162
|
-
// implementation (see https://reactjs.org/docs/portals.html).
|
|
163
|
-
// eslint-disable-next-line react/no-find-dom-node
|
|
164
|
-
return this.props.target && findDOMNode(this);
|
|
156
|
+
return typeof this.props.target === 'function'
|
|
157
|
+
? this.props.target()
|
|
158
|
+
: this.props.target;
|
|
165
159
|
}
|
|
166
160
|
|
|
167
161
|
getRenderTarget() {
|
|
168
|
-
|
|
169
|
-
|
|
162
|
+
const target =
|
|
163
|
+
typeof this.props.renderTarget === 'function'
|
|
164
|
+
? this.props.renderTarget()
|
|
165
|
+
: this.props.renderTarget;
|
|
166
|
+
|
|
167
|
+
if (target) {
|
|
168
|
+
return target;
|
|
169
|
+
}
|
|
170
|
+
if (document.body) {
|
|
171
|
+
return document.body;
|
|
170
172
|
}
|
|
171
173
|
|
|
172
|
-
|
|
174
|
+
throw new Error('Render target and fallback unavailable');
|
|
173
175
|
}
|
|
174
176
|
|
|
175
177
|
open() {
|
|
@@ -205,10 +207,9 @@ class Portal extends Component {
|
|
|
205
207
|
if (this.props.className) {
|
|
206
208
|
this.portalElement.className = this.props.className;
|
|
207
209
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
});
|
|
210
|
+
this.setState({ isVisible: true }, () =>
|
|
211
|
+
this.props.onOpen(this.portalElement),
|
|
212
|
+
);
|
|
212
213
|
}
|
|
213
214
|
|
|
214
215
|
close() {
|
|
@@ -216,8 +217,6 @@ class Portal extends Component {
|
|
|
216
217
|
return;
|
|
217
218
|
}
|
|
218
219
|
|
|
219
|
-
unmountComponentAtNode(this.portalElement);
|
|
220
|
-
|
|
221
220
|
const renderTarget = this.getRenderTarget();
|
|
222
221
|
if (renderTarget) {
|
|
223
222
|
renderTarget.removeChild(this.portalElement);
|
|
@@ -231,6 +230,7 @@ class Portal extends Component {
|
|
|
231
230
|
document.removeEventListener('keydown', this.onDocumentKeyDown);
|
|
232
231
|
|
|
233
232
|
this.portalElement = null;
|
|
233
|
+
this.setState({ isVisible: false });
|
|
234
234
|
}
|
|
235
235
|
|
|
236
236
|
// This function is taken from modernizr
|
|
@@ -252,30 +252,18 @@ class Portal extends Component {
|
|
|
252
252
|
return supportsPassiveOption;
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
this.portalElement,
|
|
265
|
-
() => {
|
|
266
|
-
if (this.props.isOpen) {
|
|
267
|
-
this.props.onRender(this.portalElement, this.getTargetElement());
|
|
268
|
-
}
|
|
269
|
-
cb();
|
|
270
|
-
},
|
|
271
|
-
);
|
|
272
|
-
} else {
|
|
273
|
-
setImmediate(cb);
|
|
255
|
+
render() {
|
|
256
|
+
const {
|
|
257
|
+
portalElement,
|
|
258
|
+
props: { children },
|
|
259
|
+
state: { isVisible },
|
|
260
|
+
} = this;
|
|
261
|
+
|
|
262
|
+
if (!isVisible || !portalElement) {
|
|
263
|
+
return null;
|
|
274
264
|
}
|
|
275
|
-
}
|
|
276
265
|
|
|
277
|
-
|
|
278
|
-
return typeof this.props.target === 'function' ? null : this.props.target;
|
|
266
|
+
return createPortal(children, portalElement);
|
|
279
267
|
}
|
|
280
268
|
}
|
|
281
269
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bpk-scrim-utils",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.10",
|
|
4
4
|
"description": "Higher order component that adds an overlay behind components and manages scroll states.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"a11y-focus-scope": "^1.1.3",
|
|
18
18
|
"a11y-focus-store": "^1.0.0",
|
|
19
19
|
"bpk-mixins": "^31.1.1",
|
|
20
|
-
"bpk-react-utils": "^
|
|
20
|
+
"bpk-react-utils": "^5.0.0"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
23
|
"react": "^16.3.0 || ^17.0.0"
|