@carbon/react 1.77.0 → 1.78.0-rc.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/.playwright/INTERNAL_AVT_REPORT_DO_NOT_USE.json +945 -858
- package/es/components/Accordion/AccordionItem.js +1 -1
- package/es/components/BadgeIndicator/index.d.ts +23 -0
- package/es/components/BadgeIndicator/index.js +45 -0
- package/es/components/Breadcrumb/Breadcrumb.d.ts +5 -0
- package/es/components/Breadcrumb/Breadcrumb.js +8 -2
- package/es/components/Button/Button.d.ts +9 -0
- package/es/components/Button/Button.js +13 -0
- package/es/components/ComboBox/ComboBox.js +13 -9
- package/es/components/Dropdown/Dropdown.d.ts +1 -1
- package/es/components/Dropdown/Dropdown.js +3 -2
- package/es/components/FileUploader/FileUploader.js +1 -1
- package/es/components/FileUploader/FileUploaderItem.js +1 -1
- package/es/components/IconButton/index.d.ts +7 -1
- package/es/components/IconButton/index.js +18 -2
- package/es/components/IconIndicator/index.d.ts +1 -1
- package/es/components/InlineLoading/InlineLoading.js +2 -5
- package/es/components/Menu/MenuItem.js +1 -1
- package/es/components/Modal/Modal.d.ts +3 -3
- package/es/components/Modal/Modal.js +9 -3
- package/es/components/Modal/next/index.d.ts +1 -5
- package/es/components/MultiSelect/MultiSelect.js +3 -2
- package/es/components/MultiSelect/MultiSelectPropTypes.d.ts +5 -2
- package/es/components/MultiSelect/tools/sorting.js +8 -7
- package/es/components/Notification/Notification.js +1 -1
- package/es/components/NumberInput/NumberInput.js +12 -12
- package/es/components/OverflowMenu/OverflowMenu.d.ts +8 -20
- package/es/components/OverflowMenu/OverflowMenu.js +7 -9
- package/es/components/OverflowMenuItem/OverflowMenuItem.js +1 -1
- package/es/components/ProgressIndicator/ProgressIndicator.js +1 -1
- package/es/components/RadioTile/RadioTile.js +1 -1
- package/es/components/Select/Select.js +11 -2
- package/es/components/ShapeIndicator/index.d.ts +29 -0
- package/es/components/ShapeIndicator/index.js +92 -0
- package/es/components/Tabs/Tabs.d.ts +6 -4
- package/es/components/Tabs/Tabs.js +27 -8
- package/es/components/Tag/OperationalTag.d.ts +1 -36
- package/es/components/Tag/OperationalTag.js +7 -5
- package/es/components/Text/Text.d.ts +11 -9
- package/es/components/Text/Text.js +6 -6
- package/es/components/Text/TextDirection.d.ts +7 -9
- package/es/components/Text/TextDirection.js +5 -2
- package/es/components/Text/TextDirectionContext.d.ts +14 -0
- package/es/components/Text/TextDirectionContext.js +6 -2
- package/es/components/Text/createTextComponent.d.ts +5 -5
- package/es/components/Text/createTextComponent.js +5 -4
- package/es/components/Text/index.d.ts +6 -7
- package/es/components/Text/index.js +3 -2
- package/es/components/Tile/Tile.d.ts +6 -0
- package/es/components/Tile/Tile.js +5 -9
- package/es/components/Toggletip/index.d.ts +3 -2
- package/es/components/Tooltip/DefinitionTooltip.d.ts +4 -0
- package/es/components/Tooltip/DefinitionTooltip.js +7 -1
- package/es/components/Tooltip/Tooltip.d.ts +5 -66
- package/es/components/Tooltip/Tooltip.js +26 -26
- package/es/components/UIShell/HeaderGlobalAction.d.ts +9 -0
- package/es/components/UIShell/HeaderGlobalAction.js +16 -1
- package/es/index.d.ts +4 -1
- package/es/index.js +2 -1
- package/es/internal/FloatingMenu.d.ts +83 -0
- package/es/internal/FloatingMenu.js +216 -408
- package/es/internal/wrapFocus.js +1 -1
- package/lib/components/Accordion/AccordionItem.js +1 -1
- package/lib/components/BadgeIndicator/index.d.ts +23 -0
- package/lib/components/BadgeIndicator/index.js +56 -0
- package/lib/components/Breadcrumb/Breadcrumb.d.ts +5 -0
- package/lib/components/Breadcrumb/Breadcrumb.js +8 -2
- package/lib/components/Button/Button.d.ts +9 -0
- package/lib/components/Button/Button.js +13 -0
- package/lib/components/ComboBox/ComboBox.js +13 -9
- package/lib/components/Dropdown/Dropdown.d.ts +1 -1
- package/lib/components/Dropdown/Dropdown.js +2 -1
- package/lib/components/FileUploader/FileUploader.js +1 -1
- package/lib/components/FileUploader/FileUploaderItem.js +1 -1
- package/lib/components/IconButton/index.d.ts +7 -1
- package/lib/components/IconButton/index.js +18 -2
- package/lib/components/IconIndicator/index.d.ts +1 -1
- package/lib/components/InlineLoading/InlineLoading.js +2 -5
- package/lib/components/Menu/MenuItem.js +1 -1
- package/lib/components/Modal/Modal.d.ts +3 -3
- package/lib/components/Modal/Modal.js +9 -3
- package/lib/components/Modal/next/index.d.ts +1 -5
- package/lib/components/MultiSelect/MultiSelect.js +2 -1
- package/lib/components/MultiSelect/MultiSelectPropTypes.d.ts +5 -2
- package/lib/components/MultiSelect/tools/sorting.js +8 -7
- package/lib/components/Notification/Notification.js +1 -1
- package/lib/components/NumberInput/NumberInput.js +12 -12
- package/lib/components/OverflowMenu/OverflowMenu.d.ts +8 -20
- package/lib/components/OverflowMenu/OverflowMenu.js +7 -9
- package/lib/components/OverflowMenuItem/OverflowMenuItem.js +1 -1
- package/lib/components/ProgressIndicator/ProgressIndicator.js +1 -1
- package/lib/components/RadioTile/RadioTile.js +1 -1
- package/lib/components/Select/Select.js +11 -2
- package/lib/components/ShapeIndicator/index.d.ts +29 -0
- package/lib/components/ShapeIndicator/index.js +104 -0
- package/lib/components/Tabs/Tabs.d.ts +6 -4
- package/lib/components/Tabs/Tabs.js +42 -23
- package/lib/components/Tag/OperationalTag.d.ts +1 -36
- package/lib/components/Tag/OperationalTag.js +6 -4
- package/lib/components/Text/Text.d.ts +11 -9
- package/lib/components/Text/Text.js +5 -5
- package/lib/components/Text/TextDirection.d.ts +7 -9
- package/lib/components/Text/TextDirection.js +5 -2
- package/lib/components/Text/TextDirectionContext.d.ts +14 -0
- package/lib/components/Text/TextDirectionContext.js +6 -2
- package/lib/components/Text/createTextComponent.d.ts +5 -5
- package/lib/components/Text/createTextComponent.js +5 -4
- package/lib/components/Text/index.d.ts +6 -7
- package/lib/components/Text/index.js +4 -3
- package/lib/components/Tile/Tile.d.ts +6 -0
- package/lib/components/Tile/Tile.js +5 -9
- package/lib/components/Toggletip/index.d.ts +3 -2
- package/lib/components/Tooltip/DefinitionTooltip.d.ts +4 -0
- package/lib/components/Tooltip/DefinitionTooltip.js +7 -1
- package/lib/components/Tooltip/Tooltip.d.ts +5 -66
- package/lib/components/Tooltip/Tooltip.js +26 -26
- package/lib/components/UIShell/HeaderGlobalAction.d.ts +9 -0
- package/lib/components/UIShell/HeaderGlobalAction.js +16 -1
- package/lib/index.d.ts +4 -1
- package/lib/index.js +42 -40
- package/lib/internal/FloatingMenu.d.ts +83 -0
- package/lib/internal/FloatingMenu.js +216 -409
- package/lib/internal/wrapFocus.js +1 -1
- package/package.json +5 -5
- package/scss/components/badge-indicator/_badge-indicator.scss +9 -0
- package/scss/components/badge-indicator/_index.scss +9 -0
|
@@ -5,93 +5,34 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import React__default from 'react';
|
|
8
|
+
import React__default, { useContext, useState, useRef, useCallback, useEffect, cloneElement } from 'react';
|
|
9
|
+
import * as FeatureFlags from '@carbon/feature-flags';
|
|
11
10
|
import ReactDOM from 'react-dom';
|
|
12
11
|
import window from 'window-or-global';
|
|
13
12
|
import OptimizedResize from './OptimizedResize.js';
|
|
14
13
|
import { selectorTabbable, selectorFocusable } from './keyboard/navigation.js';
|
|
14
|
+
import { PrefixContext } from './usePrefix.js';
|
|
15
15
|
import { warning } from './warning.js';
|
|
16
16
|
import wrapFocus, { wrapFocusWithoutSentinels } from './wrapFocus.js';
|
|
17
|
-
import { PrefixContext } from './usePrefix.js';
|
|
18
|
-
import * as FeatureFlags from '@carbon/feature-flags';
|
|
19
17
|
import { match } from './keyboard/match.js';
|
|
20
18
|
import { Tab } from './keyboard/keys.js';
|
|
21
19
|
|
|
22
|
-
/**
|
|
23
|
-
* The structure for the position of floating menu.
|
|
24
|
-
* @typedef {object} FloatingMenu~position
|
|
25
|
-
* @property {number} left The left position.
|
|
26
|
-
* @property {number} top The top position.
|
|
27
|
-
* @property {number} right The right position.
|
|
28
|
-
* @property {number} bottom The bottom position.
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* The structure for the size of floating menu.
|
|
33
|
-
* @typedef {object} FloatingMenu~size
|
|
34
|
-
* @property {number} width The width.
|
|
35
|
-
* @property {number} height The height.
|
|
36
|
-
*/
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* The structure for the position offset of floating menu.
|
|
40
|
-
* @typedef {object} FloatingMenu~offset
|
|
41
|
-
* @property {number} top The top position.
|
|
42
|
-
* @property {number} left The left position.
|
|
43
|
-
*/
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* The structure for the target container.
|
|
47
|
-
* @typedef {object} FloatingMenu~container
|
|
48
|
-
* @property {DOMRect} rect Return of element.getBoundingClientRect()
|
|
49
|
-
* @property {string} position Position style (static, absolute, relative...)
|
|
50
|
-
*/
|
|
51
|
-
|
|
52
20
|
const DIRECTION_LEFT = 'left';
|
|
53
21
|
const DIRECTION_TOP = 'top';
|
|
54
22
|
const DIRECTION_RIGHT = 'right';
|
|
55
23
|
const DIRECTION_BOTTOM = 'bottom';
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* @param {FloatingMenu~offset} [oldMenuOffset={}] The old value.
|
|
59
|
-
* @param {FloatingMenu~offset} [menuOffset={}] The new value.
|
|
60
|
-
* @returns `true` if the parent component wants to change in the adjustment of the floating menu position.
|
|
61
|
-
* @private
|
|
62
|
-
*/
|
|
63
|
-
const hasChangeInOffset = function () {
|
|
64
|
-
let oldMenuOffset = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
65
|
-
let menuOffset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
66
|
-
if (typeof oldMenuOffset !== typeof menuOffset) {
|
|
67
|
-
return true;
|
|
68
|
-
}
|
|
69
|
-
if (Object(menuOffset) === menuOffset && typeof menuOffset !== 'function') {
|
|
70
|
-
return oldMenuOffset.top !== menuOffset.top || oldMenuOffset.left !== menuOffset.left;
|
|
71
|
-
}
|
|
72
|
-
return oldMenuOffset !== menuOffset;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
24
|
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* @param {FloatingMenu~position} params.refPosition The position of the triggering element.
|
|
79
|
-
* @param {FloatingMenu~offset} [params.offset={ left: 0, top: 0 }] The position offset of the menu.
|
|
80
|
-
* @param {string} [params.direction=bottom] The menu direction.
|
|
81
|
-
* @param {number} [params.scrollX=0] The scroll position of the viewport.
|
|
82
|
-
* @param {number} [params.scrollY=0] The scroll position of the viewport.
|
|
83
|
-
* @param {FloatingMenu~container} [params.container] The size and position type of target element.
|
|
84
|
-
* @returns {FloatingMenu~offset} The position of the menu, relative to the top-left corner of the viewport.
|
|
85
|
-
* @private
|
|
25
|
+
* Computes the floating menu's position based on the menu size, trigger element
|
|
26
|
+
* position, offset, direction, and container.
|
|
86
27
|
*/
|
|
87
28
|
const getFloatingPosition = _ref => {
|
|
88
29
|
let {
|
|
89
30
|
menuSize,
|
|
90
|
-
refPosition
|
|
91
|
-
offset
|
|
92
|
-
direction
|
|
93
|
-
scrollX
|
|
94
|
-
scrollY
|
|
31
|
+
refPosition,
|
|
32
|
+
offset,
|
|
33
|
+
direction,
|
|
34
|
+
scrollX,
|
|
35
|
+
scrollY,
|
|
95
36
|
container
|
|
96
37
|
} = _ref;
|
|
97
38
|
const {
|
|
@@ -100,8 +41,8 @@ const getFloatingPosition = _ref => {
|
|
|
100
41
|
right: refRight = 0,
|
|
101
42
|
bottom: refBottom = 0
|
|
102
43
|
} = refPosition;
|
|
103
|
-
const
|
|
104
|
-
const
|
|
44
|
+
const effectiveScrollX = container.position !== 'static' ? 0 : scrollX;
|
|
45
|
+
const effectiveScrollY = container.position !== 'static' ? 0 : scrollY;
|
|
105
46
|
const relativeDiff = {
|
|
106
47
|
top: container.position !== 'static' ? container.rect.top : 0,
|
|
107
48
|
left: container.position !== 'static' ? container.rect.left : 0
|
|
@@ -116,366 +57,233 @@ const getFloatingPosition = _ref => {
|
|
|
116
57
|
} = offset;
|
|
117
58
|
const refCenterHorizontal = (refLeft + refRight) / 2;
|
|
118
59
|
const refCenterVertical = (refTop + refBottom) / 2;
|
|
119
|
-
|
|
60
|
+
const positions = {
|
|
120
61
|
[DIRECTION_LEFT]: () => ({
|
|
121
|
-
left: refLeft - width +
|
|
122
|
-
top: refCenterVertical - height / 2 +
|
|
62
|
+
left: refLeft - width + effectiveScrollX - left - relativeDiff.left,
|
|
63
|
+
top: refCenterVertical - height / 2 + effectiveScrollY + top - 9 - relativeDiff.top
|
|
123
64
|
}),
|
|
124
65
|
[DIRECTION_TOP]: () => ({
|
|
125
|
-
left: refCenterHorizontal - width / 2 +
|
|
126
|
-
top: refTop - height +
|
|
66
|
+
left: refCenterHorizontal - width / 2 + effectiveScrollX + left - relativeDiff.left,
|
|
67
|
+
top: refTop - height + effectiveScrollY - top - relativeDiff.top
|
|
127
68
|
}),
|
|
128
69
|
[DIRECTION_RIGHT]: () => ({
|
|
129
|
-
left: refRight +
|
|
130
|
-
top: refCenterVertical - height / 2 +
|
|
70
|
+
left: refRight + effectiveScrollX + left - relativeDiff.left,
|
|
71
|
+
top: refCenterVertical - height / 2 + effectiveScrollY + top + 3 - relativeDiff.top
|
|
131
72
|
}),
|
|
132
73
|
[DIRECTION_BOTTOM]: () => ({
|
|
133
|
-
left: refCenterHorizontal - width / 2 +
|
|
134
|
-
top: refBottom +
|
|
74
|
+
left: refCenterHorizontal - width / 2 + effectiveScrollX + left - relativeDiff.left,
|
|
75
|
+
top: refBottom + effectiveScrollY + top - relativeDiff.top
|
|
135
76
|
})
|
|
136
|
-
}
|
|
77
|
+
};
|
|
78
|
+
return positions[direction]();
|
|
137
79
|
};
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
*/
|
|
163
|
-
_defineProperty(this, "_menuContainer", null);
|
|
164
|
-
/**
|
|
165
|
-
* The cached reference to the menu body.
|
|
166
|
-
* The reference is set via callback ref instead of object ref,
|
|
167
|
-
* in order to hook the event when the element ref gets available,
|
|
168
|
-
* which can be at a different timing from `cDM()`, presumably with SSR scenario.
|
|
169
|
-
* @type {Element}
|
|
170
|
-
* @private
|
|
171
|
-
*/
|
|
172
|
-
_defineProperty(this, "_menuBody", null);
|
|
173
|
-
/**
|
|
174
|
-
* Focus sentinel refs for focus trap behavior
|
|
175
|
-
*/
|
|
176
|
-
_defineProperty(this, "startSentinel", /*#__PURE__*/React__default.createRef());
|
|
177
|
-
_defineProperty(this, "endSentinel", /*#__PURE__*/React__default.createRef());
|
|
178
|
-
/**
|
|
179
|
-
* Calculates the position in the viewport of floating menu,
|
|
180
|
-
* once this component is mounted or updated upon change in the following props:
|
|
181
|
-
*
|
|
182
|
-
* * `menuOffset` (The adjustment that should be applied to the calculated floating menu's position)
|
|
183
|
-
* * `menuDirection` (Where the floating menu menu should be placed relative to the trigger button)
|
|
184
|
-
*
|
|
185
|
-
* @private
|
|
186
|
-
*/
|
|
187
|
-
_defineProperty(this, "_updateMenuSize", function () {
|
|
188
|
-
let prevProps = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
189
|
-
let isAdjustment = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
190
|
-
const menuBody = _this._menuBody;
|
|
80
|
+
const FloatingMenu = _ref2 => {
|
|
81
|
+
let {
|
|
82
|
+
children,
|
|
83
|
+
flipped,
|
|
84
|
+
focusTrap,
|
|
85
|
+
menuDirection = DIRECTION_BOTTOM,
|
|
86
|
+
menuOffset = {},
|
|
87
|
+
menuRef: externalMenuRef,
|
|
88
|
+
onPlace,
|
|
89
|
+
selectorPrimaryFocus,
|
|
90
|
+
styles,
|
|
91
|
+
target = () => document.body,
|
|
92
|
+
triggerRef,
|
|
93
|
+
updateOrientation
|
|
94
|
+
} = _ref2;
|
|
95
|
+
const prefix = useContext(PrefixContext);
|
|
96
|
+
const [floatingPosition, setFloatingPosition] = useState(undefined);
|
|
97
|
+
const menuBodyRef = useRef(null);
|
|
98
|
+
const startSentinelRef = useRef(null);
|
|
99
|
+
const endSentinelRef = useRef(null);
|
|
100
|
+
const placeInProgressRef = useRef(false);
|
|
101
|
+
const updateMenuPosition = useCallback(isAdjustment => {
|
|
102
|
+
const menuBody = menuBodyRef.current;
|
|
103
|
+
if (!menuBody) {
|
|
191
104
|
process.env.NODE_ENV !== "production" ? warning(menuBody, 'The DOM node for menu body for calculating its position is not available. Skipping...') : void 0;
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
const {
|
|
210
|
-
current: triggerEl
|
|
211
|
-
} = triggerRef;
|
|
212
|
-
const menuSize = menuBody.getBoundingClientRect();
|
|
213
|
-
const refPosition = triggerEl && triggerEl.getBoundingClientRect();
|
|
214
|
-
const offset = typeof menuOffset !== 'function' ? menuOffset : menuOffset(menuBody, menuDirection, triggerEl, flipped);
|
|
215
|
-
|
|
216
|
-
// Optional function to allow parent component to check
|
|
217
|
-
// if the orientation needs to be changed based on params
|
|
218
|
-
if (updateOrientation) {
|
|
219
|
-
updateOrientation({
|
|
220
|
-
menuSize,
|
|
221
|
-
refPosition,
|
|
222
|
-
direction: menuDirection,
|
|
223
|
-
offset,
|
|
224
|
-
scrollX: window.pageXOffset,
|
|
225
|
-
scrollY: window.pageYOffset,
|
|
226
|
-
container: {
|
|
227
|
-
rect: _this.props.target().getBoundingClientRect(),
|
|
228
|
-
position: getComputedStyle(_this.props.target()).position
|
|
229
|
-
}
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
// Skips if either in the following condition:
|
|
233
|
-
// a) Menu body has `display:none`
|
|
234
|
-
// b) `menuOffset` as a callback returns `undefined` (The callback saw that it couldn't calculate the value)
|
|
235
|
-
if (menuSize.width > 0 && menuSize.height > 0 || !offset) {
|
|
236
|
-
_this.setState({
|
|
237
|
-
floatingPosition: getFloatingPosition({
|
|
238
|
-
menuSize,
|
|
239
|
-
refPosition,
|
|
240
|
-
direction: menuDirection,
|
|
241
|
-
offset,
|
|
242
|
-
scrollX: window.pageXOffset,
|
|
243
|
-
scrollY: window.pageYOffset,
|
|
244
|
-
container: {
|
|
245
|
-
rect: _this.props.target().getBoundingClientRect(),
|
|
246
|
-
position: getComputedStyle(_this.props.target()).position
|
|
247
|
-
}
|
|
248
|
-
})
|
|
249
|
-
}, () => {
|
|
250
|
-
if (!isAdjustment) {
|
|
251
|
-
const newMenuSize = menuBody.getBoundingClientRect();
|
|
252
|
-
if (newMenuSize !== menuSize) {
|
|
253
|
-
_this._updateMenuSize(_this.props, true);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
});
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const triggerEl = triggerRef.current;
|
|
108
|
+
const menuSize = menuBody.getBoundingClientRect();
|
|
109
|
+
const refPosition = triggerEl ? triggerEl.getBoundingClientRect() : undefined;
|
|
110
|
+
const offsetValue = typeof menuOffset === 'function' ? menuOffset(menuBody, menuDirection, triggerEl, flipped) : menuOffset;
|
|
111
|
+
if (updateOrientation) {
|
|
112
|
+
updateOrientation({
|
|
113
|
+
menuSize,
|
|
114
|
+
refPosition,
|
|
115
|
+
direction: menuDirection,
|
|
116
|
+
offset: offsetValue,
|
|
117
|
+
scrollX: window.pageXOffset,
|
|
118
|
+
scrollY: window.pageYOffset,
|
|
119
|
+
container: {
|
|
120
|
+
rect: target().getBoundingClientRect(),
|
|
121
|
+
position: getComputedStyle(target()).position
|
|
257
122
|
}
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
process.env.NODE_ENV !== "production" ? warning(focusableNode === null, 'Floating Menus must have at least a programmatically focusable child. ' + 'This can be accomplished by adding tabIndex="-1" to the content element.') : void 0;
|
|
279
|
-
}
|
|
280
|
-
});
|
|
281
|
-
/**
|
|
282
|
-
* A callback for called when menu body is mounted or unmounted.
|
|
283
|
-
* @param {Element} menuBody The menu body being mounted. `null` if the menu body is being unmounted.
|
|
284
|
-
*/
|
|
285
|
-
_defineProperty(this, "_menuRef", menuBody => {
|
|
286
|
-
const {
|
|
287
|
-
menuRef
|
|
288
|
-
} = this.props;
|
|
289
|
-
this._placeInProgress = !!menuBody;
|
|
290
|
-
menuRef && menuRef(this._menuBody = menuBody);
|
|
291
|
-
if (menuBody) {
|
|
292
|
-
this._updateMenuSize();
|
|
293
|
-
}
|
|
294
|
-
});
|
|
295
|
-
/**
|
|
296
|
-
* @returns The child nodes, with styles containing the floating menu position.
|
|
297
|
-
* @private
|
|
298
|
-
*/
|
|
299
|
-
_defineProperty(this, "_getChildrenWithProps", () => {
|
|
300
|
-
const {
|
|
301
|
-
styles,
|
|
302
|
-
children
|
|
303
|
-
} = this.props;
|
|
304
|
-
const {
|
|
305
|
-
floatingPosition: pos
|
|
306
|
-
} = this.state;
|
|
307
|
-
// If no pos available, we need to hide the element (offscreen to the left)
|
|
308
|
-
// This is done so we can measure the content before positioning it correctly.
|
|
309
|
-
const positioningStyle = pos ? {
|
|
310
|
-
left: `${pos.left}px`,
|
|
311
|
-
top: `${pos.top}px`,
|
|
312
|
-
right: 'auto'
|
|
313
|
-
} : {
|
|
314
|
-
visibility: 'hidden',
|
|
315
|
-
top: '0px'
|
|
316
|
-
};
|
|
317
|
-
return /*#__PURE__*/React__default.cloneElement(children, {
|
|
318
|
-
ref: this._menuRef,
|
|
319
|
-
style: {
|
|
320
|
-
...styles,
|
|
321
|
-
...positioningStyle,
|
|
322
|
-
position: 'absolute',
|
|
323
|
-
opacity: 1
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Only set position if the menu has a valid size or if no offset is provided.
|
|
127
|
+
if (menuSize.width > 0 && menuSize.height > 0 || !offsetValue) {
|
|
128
|
+
const newFloatingPosition = getFloatingPosition({
|
|
129
|
+
menuSize,
|
|
130
|
+
refPosition: refPosition ?? {
|
|
131
|
+
left: 0,
|
|
132
|
+
top: 0,
|
|
133
|
+
right: 0,
|
|
134
|
+
bottom: 0
|
|
135
|
+
},
|
|
136
|
+
offset: offsetValue,
|
|
137
|
+
direction: menuDirection,
|
|
138
|
+
scrollX: window.pageXOffset,
|
|
139
|
+
scrollY: window.pageYOffset,
|
|
140
|
+
container: {
|
|
141
|
+
rect: target().getBoundingClientRect(),
|
|
142
|
+
position: getComputedStyle(target()).position
|
|
324
143
|
}
|
|
325
144
|
});
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
* @param {Element} event.target previously focused node
|
|
331
|
-
* @param {Element} event.relatedTarget current focused node
|
|
332
|
-
*/
|
|
333
|
-
_defineProperty(this, "handleBlur", _ref2 => {
|
|
334
|
-
let {
|
|
335
|
-
target: oldActiveNode,
|
|
336
|
-
relatedTarget: currentActiveNode
|
|
337
|
-
} = _ref2;
|
|
338
|
-
if (currentActiveNode && oldActiveNode) {
|
|
339
|
-
const {
|
|
340
|
-
current: startSentinelNode
|
|
341
|
-
} = this.startSentinel;
|
|
342
|
-
const {
|
|
343
|
-
current: endSentinelNode
|
|
344
|
-
} = this.endSentinel;
|
|
345
|
-
wrapFocus({
|
|
346
|
-
bodyNode: this._menuBody,
|
|
347
|
-
startSentinelNode,
|
|
348
|
-
endSentinelNode,
|
|
349
|
-
currentActiveNode,
|
|
350
|
-
oldActiveNode
|
|
351
|
-
});
|
|
145
|
+
|
|
146
|
+
// Only update if the position has actually changed.
|
|
147
|
+
if (!floatingPosition || floatingPosition.left !== newFloatingPosition.left || floatingPosition.top !== newFloatingPosition.top) {
|
|
148
|
+
setFloatingPosition(newFloatingPosition);
|
|
352
149
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
currentActiveNode: event.target,
|
|
363
|
-
event
|
|
364
|
-
});
|
|
150
|
+
|
|
151
|
+
// Re-check after setting the position if not already adjusting.
|
|
152
|
+
if (!isAdjustment) {
|
|
153
|
+
const newMenuSize = menuBody.getBoundingClientRect();
|
|
154
|
+
// TODO: Was there a bug in the old code? How could one `DOMRect` be
|
|
155
|
+
// compared to another using `!==`?
|
|
156
|
+
if (newMenuSize.width !== menuSize.width || newMenuSize.height !== menuSize.height) {
|
|
157
|
+
updateMenuPosition(true);
|
|
158
|
+
}
|
|
365
159
|
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
160
|
+
}
|
|
161
|
+
}, [triggerRef, menuOffset, menuDirection, flipped, target, updateOrientation, floatingPosition]);
|
|
162
|
+
const focusMenuContent = menuBody => {
|
|
163
|
+
const primaryFocusNode = selectorPrimaryFocus ? menuBody.querySelector(selectorPrimaryFocus) : null;
|
|
164
|
+
const tabbableNode = menuBody.querySelector(selectorTabbable);
|
|
165
|
+
const focusableNode = menuBody.querySelector(selectorFocusable);
|
|
166
|
+
const focusTarget = primaryFocusNode ||
|
|
167
|
+
// User defined focusable node
|
|
168
|
+
tabbableNode ||
|
|
169
|
+
// First sequentially focusable node
|
|
170
|
+
focusableNode ||
|
|
171
|
+
// First programmatic focusable node
|
|
172
|
+
menuBody;
|
|
173
|
+
focusTarget.focus();
|
|
174
|
+
if (focusTarget === menuBody && process.env.NODE_ENV !== "production") {
|
|
175
|
+
process.env.NODE_ENV !== "production" ? warning(focusableNode === null, 'Floating Menus must have at least a programmatically focusable child. This can be accomplished by adding tabIndex="-1" to the content element.') : void 0;
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
const handleMenuRef = node => {
|
|
179
|
+
menuBodyRef.current = node;
|
|
180
|
+
placeInProgressRef.current = !!node;
|
|
181
|
+
if (externalMenuRef) {
|
|
182
|
+
externalMenuRef(node);
|
|
183
|
+
}
|
|
184
|
+
if (node) {
|
|
185
|
+
updateMenuPosition();
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// When the menu has been placed, focus the content and call onPlace.
|
|
190
|
+
useEffect(() => {
|
|
191
|
+
if (placeInProgressRef.current && floatingPosition && menuBodyRef.current) {
|
|
192
|
+
if (!menuBodyRef.current.contains(document.activeElement)) {
|
|
193
|
+
focusMenuContent(menuBodyRef.current);
|
|
384
194
|
}
|
|
385
195
|
if (typeof onPlace === 'function') {
|
|
386
|
-
onPlace(
|
|
387
|
-
this._placeInProgress = false;
|
|
196
|
+
onPlace(menuBodyRef.current);
|
|
388
197
|
}
|
|
198
|
+
placeInProgressRef.current = false;
|
|
389
199
|
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
}, !focusTrapWithoutSentinels && /*#__PURE__*/React__default.createElement("span", {
|
|
408
|
-
ref: this.startSentinel,
|
|
409
|
-
tabIndex: "0",
|
|
410
|
-
role: "link",
|
|
411
|
-
className: `${prefix}--visually-hidden`
|
|
412
|
-
}, "Focus sentinel"), this._getChildrenWithProps(), !focusTrapWithoutSentinels && /*#__PURE__*/React__default.createElement("span", {
|
|
413
|
-
ref: this.endSentinel,
|
|
414
|
-
tabIndex: "0",
|
|
415
|
-
role: "link",
|
|
416
|
-
className: `${prefix}--visually-hidden`
|
|
417
|
-
}, "Focus sentinel")), !target ? document.body : target());
|
|
418
|
-
}
|
|
419
|
-
return null;
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
_defineProperty(FloatingMenu, "contextType", PrefixContext);
|
|
423
|
-
_defineProperty(FloatingMenu, "propTypes", {
|
|
424
|
-
/**
|
|
425
|
-
* Contents to put into the floating menu.
|
|
426
|
-
*/
|
|
427
|
-
children: PropTypes.object,
|
|
428
|
-
/**
|
|
429
|
-
* `true` if the menu alignment should be flipped.
|
|
430
|
-
*/
|
|
431
|
-
flipped: PropTypes.bool,
|
|
432
|
-
/**
|
|
433
|
-
* Enable or disable focus trap behavior
|
|
434
|
-
*/
|
|
435
|
-
focusTrap: PropTypes.bool,
|
|
436
|
-
/**
|
|
437
|
-
* Where to put the tooltip, relative to the trigger button.
|
|
438
|
-
*/
|
|
439
|
-
menuDirection: PropTypes.oneOf([DIRECTION_LEFT, DIRECTION_TOP, DIRECTION_RIGHT, DIRECTION_BOTTOM]),
|
|
440
|
-
/**
|
|
441
|
-
* The adjustment of the floating menu position, considering the position of dropdown arrow, etc.
|
|
442
|
-
*/
|
|
443
|
-
menuOffset: PropTypes.oneOfType([PropTypes.shape({
|
|
444
|
-
top: PropTypes.number,
|
|
445
|
-
left: PropTypes.number
|
|
446
|
-
}), PropTypes.func]),
|
|
447
|
-
/**
|
|
448
|
-
* The callback called when the menu body has been mounted to/will be unmounted from the DOM.
|
|
449
|
-
*/
|
|
450
|
-
menuRef: PropTypes.func,
|
|
451
|
-
/**
|
|
452
|
-
* The callback called when the menu body has been mounted and positioned.
|
|
453
|
-
*/
|
|
454
|
-
onPlace: PropTypes.func,
|
|
455
|
-
/**
|
|
456
|
-
* Specify a CSS selector that matches the DOM element that should
|
|
457
|
-
* be focused when the Modal opens
|
|
458
|
-
*/
|
|
459
|
-
selectorPrimaryFocus: PropTypes.string,
|
|
460
|
-
/**
|
|
461
|
-
* The additional styles to put to the floating menu.
|
|
462
|
-
*/
|
|
463
|
-
styles: PropTypes.object,
|
|
200
|
+
}, [floatingPosition, onPlace]);
|
|
201
|
+
|
|
202
|
+
// Attach a resize listener.
|
|
203
|
+
useEffect(() => {
|
|
204
|
+
const resizeHandler = OptimizedResize.add(() => {
|
|
205
|
+
updateMenuPosition();
|
|
206
|
+
});
|
|
207
|
+
return () => {
|
|
208
|
+
resizeHandler.release();
|
|
209
|
+
};
|
|
210
|
+
}, [triggerRef, menuOffset, menuDirection, flipped, target, updateOrientation]);
|
|
211
|
+
|
|
212
|
+
// Update menu position when key props change.
|
|
213
|
+
useEffect(() => {
|
|
214
|
+
updateMenuPosition();
|
|
215
|
+
}, [menuOffset, menuDirection, flipped, triggerRef, target, updateOrientation]);
|
|
216
|
+
|
|
464
217
|
/**
|
|
465
|
-
*
|
|
218
|
+
* Clones the child element to add a `ref` and positioning styles.
|
|
466
219
|
*/
|
|
467
|
-
|
|
220
|
+
const getChildrenWithProps = () => {
|
|
221
|
+
const pos = floatingPosition;
|
|
222
|
+
const positioningStyle = pos ? {
|
|
223
|
+
left: `${pos.left}px`,
|
|
224
|
+
top: `${pos.top}px`,
|
|
225
|
+
right: 'auto'
|
|
226
|
+
} : {
|
|
227
|
+
visibility: 'hidden',
|
|
228
|
+
top: '0px'
|
|
229
|
+
};
|
|
230
|
+
return /*#__PURE__*/cloneElement(children, {
|
|
231
|
+
ref: handleMenuRef,
|
|
232
|
+
style: {
|
|
233
|
+
...styles,
|
|
234
|
+
...positioningStyle,
|
|
235
|
+
position: 'absolute',
|
|
236
|
+
opacity: 1
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
};
|
|
240
|
+
|
|
468
241
|
/**
|
|
469
|
-
*
|
|
242
|
+
* Blur handler used when focus trapping is enabled.
|
|
470
243
|
*/
|
|
471
|
-
|
|
472
|
-
current
|
|
473
|
-
|
|
244
|
+
const handleBlur = event => {
|
|
245
|
+
if (menuBodyRef.current && startSentinelRef.current && endSentinelRef.current) {
|
|
246
|
+
wrapFocus({
|
|
247
|
+
bodyNode: menuBodyRef.current,
|
|
248
|
+
startTrapNode: startSentinelRef.current,
|
|
249
|
+
endTrapNode: endSentinelRef.current,
|
|
250
|
+
currentActiveNode: event.relatedTarget,
|
|
251
|
+
oldActiveNode: event.target
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
|
|
474
256
|
/**
|
|
475
|
-
*
|
|
257
|
+
* Keydown handler for focus wrapping when experimental focus trap is enabled.
|
|
476
258
|
*/
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
259
|
+
const handleKeyDown = event => {
|
|
260
|
+
if (match(event, Tab) && menuBodyRef.current) {
|
|
261
|
+
wrapFocusWithoutSentinels({
|
|
262
|
+
containerNode: menuBodyRef.current,
|
|
263
|
+
currentActiveNode: event.target,
|
|
264
|
+
event
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
const focusTrapWithoutSentinels = FeatureFlags.enabled('enable-experimental-focus-wrap-without-sentinels');
|
|
269
|
+
if (typeof document !== 'undefined') {
|
|
270
|
+
const portalTarget = target ? target() : document.body;
|
|
271
|
+
return /*#__PURE__*/ReactDOM.createPortal(/*#__PURE__*/React__default.createElement("div", {
|
|
272
|
+
onBlur: focusTrap && !focusTrapWithoutSentinels ? handleBlur : undefined,
|
|
273
|
+
onKeyDown: focusTrapWithoutSentinels ? handleKeyDown : undefined
|
|
274
|
+
}, !focusTrapWithoutSentinels && /*#__PURE__*/React__default.createElement("span", {
|
|
275
|
+
ref: startSentinelRef,
|
|
276
|
+
tabIndex: 0,
|
|
277
|
+
role: "link",
|
|
278
|
+
className: `${prefix}--visually-hidden`
|
|
279
|
+
}, "Focus sentinel"), getChildrenWithProps(), !focusTrapWithoutSentinels && /*#__PURE__*/React__default.createElement("span", {
|
|
280
|
+
ref: endSentinelRef,
|
|
281
|
+
tabIndex: 0,
|
|
282
|
+
role: "link",
|
|
283
|
+
className: `${prefix}--visually-hidden`
|
|
284
|
+
}, "Focus sentinel")), portalTarget);
|
|
285
|
+
}
|
|
286
|
+
return null;
|
|
287
|
+
};
|
|
480
288
|
|
|
481
|
-
export { DIRECTION_BOTTOM, DIRECTION_LEFT, DIRECTION_RIGHT, DIRECTION_TOP, FloatingMenu
|
|
289
|
+
export { DIRECTION_BOTTOM, DIRECTION_LEFT, DIRECTION_RIGHT, DIRECTION_TOP, FloatingMenu };
|