carbon-react 106.5.0 → 106.6.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/esm/components/multi-action-button/multi-action-button.component.js +69 -47
- package/esm/components/multi-action-button/multi-action-button.style.js +7 -1
- package/esm/components/split-button/split-button-toggle.style.js +9 -0
- package/esm/components/split-button/split-button.component.js +62 -30
- package/lib/components/multi-action-button/multi-action-button.component.js +69 -47
- package/lib/components/multi-action-button/multi-action-button.style.js +7 -1
- package/lib/components/split-button/split-button-toggle.style.js +9 -0
- package/lib/components/split-button/split-button.component.js +64 -30
- package/package.json +2 -2
|
@@ -35,15 +35,22 @@ const MultiActionButton = ({
|
|
|
35
35
|
const ref = useRef();
|
|
36
36
|
const buttonRef = useRef();
|
|
37
37
|
const buttonContainer = useRef();
|
|
38
|
-
const userInputType = "ontouchstart" in document.documentElement ? "touchstart" : "click";
|
|
39
38
|
const buttonChildren = React.Children.toArray(children);
|
|
40
39
|
const additionalButtons = useRef(buttonChildren.map(() => /*#__PURE__*/React.createRef()));
|
|
41
40
|
const listening = useRef(false);
|
|
42
41
|
const isMainButtonFocused = useRef(false);
|
|
42
|
+
const isFocusedAfterClosing = useRef(false);
|
|
43
43
|
const [showAdditionalButtons, setShowAdditionalButtons] = useState(false);
|
|
44
|
-
const [removeHandleClickOutside, setRemoveHandleClickOutside] = useState(false);
|
|
45
|
-
const [removeHandleKeyDown, setRemoveHandleKeyDown] = useState(false);
|
|
46
44
|
const [minWidth, setMinWidth] = useState(0);
|
|
45
|
+
const hideButtons = useCallback(() => {
|
|
46
|
+
if (isMainButtonFocused.current) return;
|
|
47
|
+
setShowAdditionalButtons(false);
|
|
48
|
+
}, []);
|
|
49
|
+
|
|
50
|
+
const showButtons = () => {
|
|
51
|
+
setShowAdditionalButtons(true);
|
|
52
|
+
setMinWidth(ref.current.getBoundingClientRect().width);
|
|
53
|
+
};
|
|
47
54
|
|
|
48
55
|
const childrenWithProps = () => {
|
|
49
56
|
return buttonChildren.map((child, index) => {
|
|
@@ -51,7 +58,16 @@ const MultiActionButton = ({
|
|
|
51
58
|
key: index.toString(),
|
|
52
59
|
role: "menuitem",
|
|
53
60
|
ref: additionalButtons.current[index],
|
|
54
|
-
tabIndex: -1
|
|
61
|
+
tabIndex: -1,
|
|
62
|
+
onClick: ev => {
|
|
63
|
+
var _buttonRef$current;
|
|
64
|
+
|
|
65
|
+
if (child.props.onClick) child.props.onClick(ev);
|
|
66
|
+
isMainButtonFocused.current = false;
|
|
67
|
+
hideButtons();
|
|
68
|
+
isFocusedAfterClosing.current = true;
|
|
69
|
+
(_buttonRef$current = buttonRef.current) === null || _buttonRef$current === void 0 ? void 0 : _buttonRef$current.focus();
|
|
70
|
+
}
|
|
55
71
|
};
|
|
56
72
|
|
|
57
73
|
if (child.type === Button) {
|
|
@@ -62,17 +78,6 @@ const MultiActionButton = ({
|
|
|
62
78
|
});
|
|
63
79
|
};
|
|
64
80
|
|
|
65
|
-
const hideButtons = useCallback(() => {
|
|
66
|
-
if (isMainButtonFocused.current) return;
|
|
67
|
-
setShowAdditionalButtons(false);
|
|
68
|
-
setRemoveHandleClickOutside(true);
|
|
69
|
-
/* istanbul ignore else */
|
|
70
|
-
|
|
71
|
-
if (listening.current) {
|
|
72
|
-
setRemoveHandleKeyDown(true);
|
|
73
|
-
listening.current = false;
|
|
74
|
-
}
|
|
75
|
-
}, []);
|
|
76
81
|
const handleKeyDown = useCallback(ev => {
|
|
77
82
|
const currentIndex = additionalButtons.current.findIndex(node => node.current === document.activeElement);
|
|
78
83
|
let nextIndex = -1;
|
|
@@ -85,7 +90,9 @@ const MultiActionButton = ({
|
|
|
85
90
|
if (Events.isDownKey(ev)) {
|
|
86
91
|
nextIndex = currentIndex < children.length - 1 ? currentIndex + 1 : 0;
|
|
87
92
|
ev.preventDefault();
|
|
88
|
-
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (Events.isTabKey(ev)) {
|
|
89
96
|
var _elements$indexOf;
|
|
90
97
|
|
|
91
98
|
const elements = Array.from(document.querySelectorAll(defaultFocusableSelectors)).filter(el => Number(el.tabIndex) !== -1);
|
|
@@ -98,47 +105,79 @@ const MultiActionButton = ({
|
|
|
98
105
|
if (nextIndex > -1) {
|
|
99
106
|
additionalButtons.current[nextIndex].current.focus();
|
|
100
107
|
}
|
|
101
|
-
}, [children
|
|
108
|
+
}, [children, hideButtons]);
|
|
102
109
|
const handleClickOutside = useCallback(({
|
|
103
110
|
target
|
|
104
111
|
}) => {
|
|
105
112
|
if (!ref.current.contains(target) && buttonContainer.current && !buttonContainer.current.contains(target)) {
|
|
106
|
-
hideButtons(
|
|
113
|
+
hideButtons();
|
|
107
114
|
}
|
|
108
115
|
}, [hideButtons]);
|
|
109
|
-
|
|
110
|
-
const showButtons = () => {
|
|
111
|
-
document.addEventListener(userInputType, handleClickOutside);
|
|
112
|
-
setShowAdditionalButtons(true);
|
|
113
|
-
setMinWidth(ref.current.getBoundingClientRect().width);
|
|
116
|
+
const addListeners = useCallback(() => {
|
|
114
117
|
/* istanbul ignore else */
|
|
115
|
-
|
|
116
118
|
if (!listening.current) {
|
|
119
|
+
document.addEventListener("click", handleClickOutside);
|
|
117
120
|
document.addEventListener("keydown", handleKeyDown);
|
|
118
121
|
listening.current = true;
|
|
119
122
|
}
|
|
120
|
-
};
|
|
123
|
+
}, [handleKeyDown, handleClickOutside]);
|
|
124
|
+
const removeListeners = useCallback(() => {
|
|
125
|
+
/* istanbul ignore else */
|
|
126
|
+
if (listening.current) {
|
|
127
|
+
document.removeEventListener("click", handleClickOutside);
|
|
128
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
129
|
+
listening.current = false;
|
|
130
|
+
}
|
|
131
|
+
}, [handleKeyDown, handleClickOutside]);
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
if (showAdditionalButtons) {
|
|
134
|
+
addListeners();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return () => {
|
|
138
|
+
removeListeners();
|
|
139
|
+
};
|
|
140
|
+
}, [showAdditionalButtons, addListeners, removeListeners]);
|
|
121
141
|
|
|
122
142
|
const handleMainButtonKeyDown = ev => {
|
|
123
|
-
if (Events.isEnterKey(ev) || Events.isSpaceKey(ev)) {
|
|
124
|
-
|
|
143
|
+
if (Events.isEnterKey(ev) || Events.isSpaceKey(ev) || Events.isDownKey(ev)) {
|
|
144
|
+
ev.preventDefault();
|
|
145
|
+
|
|
146
|
+
if (!showAdditionalButtons) {
|
|
147
|
+
showButtons();
|
|
148
|
+
} // see if setTimeout could be removed after we update react to v18 thanks to the concurrent mode
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
setTimeout(() => {
|
|
152
|
+
var _additionalButtons$cu, _additionalButtons$cu2;
|
|
153
|
+
|
|
154
|
+
(_additionalButtons$cu = additionalButtons.current[0]) === null || _additionalButtons$cu === void 0 ? void 0 : (_additionalButtons$cu2 = _additionalButtons$cu.current) === null || _additionalButtons$cu2 === void 0 ? void 0 : _additionalButtons$cu2.focus();
|
|
155
|
+
}, 0);
|
|
125
156
|
}
|
|
126
157
|
};
|
|
127
158
|
|
|
128
159
|
const focusMainButton = () => {
|
|
129
160
|
isMainButtonFocused.current = true;
|
|
161
|
+
|
|
162
|
+
if (isFocusedAfterClosing.current) {
|
|
163
|
+
isFocusedAfterClosing.current = false;
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
130
167
|
showButtons();
|
|
131
168
|
};
|
|
132
169
|
|
|
170
|
+
const blurMainButton = () => {
|
|
171
|
+
isMainButtonFocused.current = false;
|
|
172
|
+
};
|
|
173
|
+
|
|
133
174
|
const mainButtonProps = () => {
|
|
134
175
|
const opts = {
|
|
135
176
|
disabled,
|
|
136
177
|
displayed: showAdditionalButtons,
|
|
137
178
|
onTouchStart: showButtons,
|
|
138
179
|
onFocus: focusMainButton,
|
|
139
|
-
onBlur:
|
|
140
|
-
isMainButtonFocused.current = false;
|
|
141
|
-
},
|
|
180
|
+
onBlur: blurMainButton,
|
|
142
181
|
onKeyDown: handleMainButtonKeyDown,
|
|
143
182
|
buttonType: buttonType || as,
|
|
144
183
|
size,
|
|
@@ -165,23 +204,6 @@ const MultiActionButton = ({
|
|
|
165
204
|
ref: buttonContainer
|
|
166
205
|
}, childrenWithProps()));
|
|
167
206
|
|
|
168
|
-
useEffect(() => {
|
|
169
|
-
if (removeHandleClickOutside) {
|
|
170
|
-
setRemoveHandleClickOutside(false);
|
|
171
|
-
document.removeEventListener(userInputType, handleClickOutside);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (removeHandleKeyDown) {
|
|
175
|
-
setRemoveHandleKeyDown(false);
|
|
176
|
-
document.removeEventListener("keydown", handleKeyDown);
|
|
177
|
-
}
|
|
178
|
-
}, [handleClickOutside, handleKeyDown, removeHandleClickOutside, removeHandleKeyDown, userInputType]);
|
|
179
|
-
useEffect(() => {
|
|
180
|
-
return () => {
|
|
181
|
-
document.removeEventListener(userInputType, handleClickOutside);
|
|
182
|
-
document.removeEventListener("keydown", handleKeyDown);
|
|
183
|
-
};
|
|
184
|
-
}, [handleClickOutside, handleKeyDown, userInputType]);
|
|
185
207
|
return /*#__PURE__*/React.createElement(StyledMultiActionButton, _extends({
|
|
186
208
|
"aria-haspopup": "true",
|
|
187
209
|
onMouseLeave: hideButtons,
|
|
@@ -18,9 +18,15 @@ const StyledMultiActionButton = styled.div`
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
&:focus {
|
|
21
|
+
background-color: var(--colorsActionMajor700);
|
|
21
22
|
border: 3px solid var(--colorsSemanticFocus500);
|
|
22
23
|
outline: none;
|
|
23
|
-
margin: -1px;
|
|
24
|
+
margin: 0 -1px;
|
|
25
|
+
|
|
26
|
+
&,
|
|
27
|
+
${StyledIcon} {
|
|
28
|
+
color: var(--colorsActionMajorYang100);
|
|
29
|
+
}
|
|
24
30
|
}
|
|
25
31
|
|
|
26
32
|
${({
|
|
@@ -53,6 +53,15 @@ const StyledSplitButtonToggle = styled(StyledButton)`
|
|
|
53
53
|
${StyledButton} + & ${StyledIcon} {
|
|
54
54
|
margin-left: 0;
|
|
55
55
|
}
|
|
56
|
+
|
|
57
|
+
&:focus {
|
|
58
|
+
background-color: var(--colorsActionMajor500);
|
|
59
|
+
|
|
60
|
+
&,
|
|
61
|
+
${StyledIcon} {
|
|
62
|
+
color: var(--colorsActionMajorYang100);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
56
65
|
`}
|
|
57
66
|
`;
|
|
58
67
|
export default StyledSplitButtonToggle;
|
|
@@ -44,14 +44,16 @@ const SplitButton = ({
|
|
|
44
44
|
|
|
45
45
|
const theme = useContext(ThemeContext) || baseTheme;
|
|
46
46
|
const isToggleButtonFocused = useRef(false);
|
|
47
|
+
const isFocusedAfterClosing = useRef(false);
|
|
47
48
|
const buttonLabelId = useRef(guid());
|
|
48
|
-
const
|
|
49
|
+
const buttonChildren = React.Children.toArray(children);
|
|
50
|
+
const additionalButtons = useRef(buttonChildren.map(() => /*#__PURE__*/React.createRef()));
|
|
49
51
|
const splitButtonNode = useRef();
|
|
50
52
|
const toggleButton = useRef();
|
|
51
53
|
const buttonContainer = useRef();
|
|
52
54
|
const [showAdditionalButtons, setShowAdditionalButtons] = useState(false);
|
|
53
55
|
const [minWidth, setMinWidth] = useState(0);
|
|
54
|
-
const
|
|
56
|
+
const listening = useRef(false);
|
|
55
57
|
const hideButtons = useCallback(() => {
|
|
56
58
|
if (isToggleButtonFocused.current) return;
|
|
57
59
|
setShowAdditionalButtons(false);
|
|
@@ -64,12 +66,8 @@ const SplitButton = ({
|
|
|
64
66
|
}
|
|
65
67
|
}, [hideButtons]);
|
|
66
68
|
const handleKeyDown = useCallback(ev => {
|
|
67
|
-
if (!showAdditionalButtons) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
69
|
const numOfChildren = children.length - 1;
|
|
72
|
-
const currentIndex = additionalButtons.current.findIndex(
|
|
70
|
+
const currentIndex = additionalButtons.current.findIndex(node => node.current === document.activeElement);
|
|
73
71
|
let nextIndex = -1;
|
|
74
72
|
|
|
75
73
|
if (Events.isUpKey(ev)) {
|
|
@@ -80,7 +78,9 @@ const SplitButton = ({
|
|
|
80
78
|
if (Events.isDownKey(ev)) {
|
|
81
79
|
nextIndex = currentIndex < numOfChildren ? currentIndex + 1 : 0;
|
|
82
80
|
ev.preventDefault();
|
|
83
|
-
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (Events.isTabKey(ev)) {
|
|
84
84
|
var _elements$indexOf;
|
|
85
85
|
|
|
86
86
|
const elements = Array.from(document.querySelectorAll(defaultFocusableSelectors)).filter(el => Number(el.tabIndex) !== -1);
|
|
@@ -91,18 +91,34 @@ const SplitButton = ({
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
if (nextIndex > -1) {
|
|
94
|
-
additionalButtons.current[nextIndex].focus();
|
|
94
|
+
additionalButtons.current[nextIndex].current.focus();
|
|
95
95
|
}
|
|
96
|
-
}, [hideButtons, children
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
96
|
+
}, [hideButtons, children]);
|
|
97
|
+
const addListeners = useCallback(() => {
|
|
98
|
+
/* istanbul ignore else */
|
|
99
|
+
if (!listening.current) {
|
|
100
|
+
document.addEventListener("click", handleClickOutside);
|
|
101
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
102
|
+
listening.current = true;
|
|
103
|
+
}
|
|
104
|
+
}, [handleKeyDown, handleClickOutside]);
|
|
105
|
+
const removeListeners = useCallback(() => {
|
|
106
|
+
/* istanbul ignore else */
|
|
107
|
+
if (listening.current) {
|
|
108
|
+
document.removeEventListener("click", handleClickOutside);
|
|
103
109
|
document.removeEventListener("keydown", handleKeyDown);
|
|
110
|
+
listening.current = false;
|
|
111
|
+
}
|
|
112
|
+
}, [handleKeyDown, handleClickOutside]);
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
if (showAdditionalButtons) {
|
|
115
|
+
addListeners();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return () => {
|
|
119
|
+
removeListeners();
|
|
104
120
|
};
|
|
105
|
-
}, [
|
|
121
|
+
}, [showAdditionalButtons, addListeners, removeListeners]);
|
|
106
122
|
|
|
107
123
|
function mainButtonProps() {
|
|
108
124
|
return {
|
|
@@ -150,11 +166,6 @@ const SplitButton = ({
|
|
|
150
166
|
};
|
|
151
167
|
}
|
|
152
168
|
|
|
153
|
-
function addRef(ref, index) {
|
|
154
|
-
if (!ref) return;
|
|
155
|
-
additionalButtons.current[index] = ref;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
169
|
function getIconColor() {
|
|
159
170
|
const colorsMap = {
|
|
160
171
|
primary: theme.colors.white,
|
|
@@ -191,8 +202,18 @@ const SplitButton = ({
|
|
|
191
202
|
}
|
|
192
203
|
|
|
193
204
|
function handleToggleButtonKeyDown(ev) {
|
|
194
|
-
if (Events.isEnterKey(ev) || Events.isSpaceKey(ev)) {
|
|
195
|
-
|
|
205
|
+
if (Events.isEnterKey(ev) || Events.isSpaceKey(ev) || Events.isDownKey(ev)) {
|
|
206
|
+
ev.preventDefault();
|
|
207
|
+
|
|
208
|
+
if (!showAdditionalButtons) {
|
|
209
|
+
showButtons();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
setTimeout(() => {
|
|
213
|
+
var _additionalButtons$cu, _additionalButtons$cu2;
|
|
214
|
+
|
|
215
|
+
(_additionalButtons$cu = additionalButtons.current[0]) === null || _additionalButtons$cu === void 0 ? void 0 : (_additionalButtons$cu2 = _additionalButtons$cu.current) === null || _additionalButtons$cu2 === void 0 ? void 0 : _additionalButtons$cu2.focus();
|
|
216
|
+
}, 0);
|
|
196
217
|
}
|
|
197
218
|
}
|
|
198
219
|
|
|
@@ -202,8 +223,17 @@ const SplitButton = ({
|
|
|
202
223
|
const childProps = {
|
|
203
224
|
key: index.toString(),
|
|
204
225
|
role: "menuitem",
|
|
205
|
-
ref:
|
|
206
|
-
tabIndex: -1
|
|
226
|
+
ref: additionalButtons.current[index],
|
|
227
|
+
tabIndex: -1,
|
|
228
|
+
onClick: ev => {
|
|
229
|
+
var _toggleButton$current;
|
|
230
|
+
|
|
231
|
+
if (child.props.onClick) child.props.onClick(ev);
|
|
232
|
+
isToggleButtonFocused.current = false;
|
|
233
|
+
hideButtons();
|
|
234
|
+
isFocusedAfterClosing.current = true;
|
|
235
|
+
(_toggleButton$current = toggleButton.current) === null || _toggleButton$current === void 0 ? void 0 : _toggleButton$current.focus();
|
|
236
|
+
}
|
|
207
237
|
};
|
|
208
238
|
|
|
209
239
|
if (child.type === Button) {
|
|
@@ -216,11 +246,13 @@ const SplitButton = ({
|
|
|
216
246
|
|
|
217
247
|
function focusToggleButton() {
|
|
218
248
|
isToggleButtonFocused.current = true;
|
|
219
|
-
showButtons();
|
|
220
|
-
}
|
|
221
249
|
|
|
222
|
-
|
|
223
|
-
|
|
250
|
+
if (isFocusedAfterClosing.current) {
|
|
251
|
+
isFocusedAfterClosing.current = false;
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
showButtons();
|
|
224
256
|
}
|
|
225
257
|
|
|
226
258
|
function renderAdditionalButtons() {
|
|
@@ -59,17 +59,24 @@ const MultiActionButton = ({
|
|
|
59
59
|
const ref = (0, _react.useRef)();
|
|
60
60
|
const buttonRef = (0, _react.useRef)();
|
|
61
61
|
const buttonContainer = (0, _react.useRef)();
|
|
62
|
-
const userInputType = "ontouchstart" in document.documentElement ? "touchstart" : "click";
|
|
63
62
|
|
|
64
63
|
const buttonChildren = _react.default.Children.toArray(children);
|
|
65
64
|
|
|
66
65
|
const additionalButtons = (0, _react.useRef)(buttonChildren.map(() => /*#__PURE__*/_react.default.createRef()));
|
|
67
66
|
const listening = (0, _react.useRef)(false);
|
|
68
67
|
const isMainButtonFocused = (0, _react.useRef)(false);
|
|
68
|
+
const isFocusedAfterClosing = (0, _react.useRef)(false);
|
|
69
69
|
const [showAdditionalButtons, setShowAdditionalButtons] = (0, _react.useState)(false);
|
|
70
|
-
const [removeHandleClickOutside, setRemoveHandleClickOutside] = (0, _react.useState)(false);
|
|
71
|
-
const [removeHandleKeyDown, setRemoveHandleKeyDown] = (0, _react.useState)(false);
|
|
72
70
|
const [minWidth, setMinWidth] = (0, _react.useState)(0);
|
|
71
|
+
const hideButtons = (0, _react.useCallback)(() => {
|
|
72
|
+
if (isMainButtonFocused.current) return;
|
|
73
|
+
setShowAdditionalButtons(false);
|
|
74
|
+
}, []);
|
|
75
|
+
|
|
76
|
+
const showButtons = () => {
|
|
77
|
+
setShowAdditionalButtons(true);
|
|
78
|
+
setMinWidth(ref.current.getBoundingClientRect().width);
|
|
79
|
+
};
|
|
73
80
|
|
|
74
81
|
const childrenWithProps = () => {
|
|
75
82
|
return buttonChildren.map((child, index) => {
|
|
@@ -77,7 +84,16 @@ const MultiActionButton = ({
|
|
|
77
84
|
key: index.toString(),
|
|
78
85
|
role: "menuitem",
|
|
79
86
|
ref: additionalButtons.current[index],
|
|
80
|
-
tabIndex: -1
|
|
87
|
+
tabIndex: -1,
|
|
88
|
+
onClick: ev => {
|
|
89
|
+
var _buttonRef$current;
|
|
90
|
+
|
|
91
|
+
if (child.props.onClick) child.props.onClick(ev);
|
|
92
|
+
isMainButtonFocused.current = false;
|
|
93
|
+
hideButtons();
|
|
94
|
+
isFocusedAfterClosing.current = true;
|
|
95
|
+
(_buttonRef$current = buttonRef.current) === null || _buttonRef$current === void 0 ? void 0 : _buttonRef$current.focus();
|
|
96
|
+
}
|
|
81
97
|
};
|
|
82
98
|
|
|
83
99
|
if (child.type === _button.default) {
|
|
@@ -88,17 +104,6 @@ const MultiActionButton = ({
|
|
|
88
104
|
});
|
|
89
105
|
};
|
|
90
106
|
|
|
91
|
-
const hideButtons = (0, _react.useCallback)(() => {
|
|
92
|
-
if (isMainButtonFocused.current) return;
|
|
93
|
-
setShowAdditionalButtons(false);
|
|
94
|
-
setRemoveHandleClickOutside(true);
|
|
95
|
-
/* istanbul ignore else */
|
|
96
|
-
|
|
97
|
-
if (listening.current) {
|
|
98
|
-
setRemoveHandleKeyDown(true);
|
|
99
|
-
listening.current = false;
|
|
100
|
-
}
|
|
101
|
-
}, []);
|
|
102
107
|
const handleKeyDown = (0, _react.useCallback)(ev => {
|
|
103
108
|
const currentIndex = additionalButtons.current.findIndex(node => node.current === document.activeElement);
|
|
104
109
|
let nextIndex = -1;
|
|
@@ -111,7 +116,9 @@ const MultiActionButton = ({
|
|
|
111
116
|
if (_events.default.isDownKey(ev)) {
|
|
112
117
|
nextIndex = currentIndex < children.length - 1 ? currentIndex + 1 : 0;
|
|
113
118
|
ev.preventDefault();
|
|
114
|
-
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (_events.default.isTabKey(ev)) {
|
|
115
122
|
var _elements$indexOf;
|
|
116
123
|
|
|
117
124
|
const elements = Array.from(document.querySelectorAll(_focusTrapUtils.defaultFocusableSelectors)).filter(el => Number(el.tabIndex) !== -1);
|
|
@@ -124,47 +131,79 @@ const MultiActionButton = ({
|
|
|
124
131
|
if (nextIndex > -1) {
|
|
125
132
|
additionalButtons.current[nextIndex].current.focus();
|
|
126
133
|
}
|
|
127
|
-
}, [children
|
|
134
|
+
}, [children, hideButtons]);
|
|
128
135
|
const handleClickOutside = (0, _react.useCallback)(({
|
|
129
136
|
target
|
|
130
137
|
}) => {
|
|
131
138
|
if (!ref.current.contains(target) && buttonContainer.current && !buttonContainer.current.contains(target)) {
|
|
132
|
-
hideButtons(
|
|
139
|
+
hideButtons();
|
|
133
140
|
}
|
|
134
141
|
}, [hideButtons]);
|
|
135
|
-
|
|
136
|
-
const showButtons = () => {
|
|
137
|
-
document.addEventListener(userInputType, handleClickOutside);
|
|
138
|
-
setShowAdditionalButtons(true);
|
|
139
|
-
setMinWidth(ref.current.getBoundingClientRect().width);
|
|
142
|
+
const addListeners = (0, _react.useCallback)(() => {
|
|
140
143
|
/* istanbul ignore else */
|
|
141
|
-
|
|
142
144
|
if (!listening.current) {
|
|
145
|
+
document.addEventListener("click", handleClickOutside);
|
|
143
146
|
document.addEventListener("keydown", handleKeyDown);
|
|
144
147
|
listening.current = true;
|
|
145
148
|
}
|
|
146
|
-
};
|
|
149
|
+
}, [handleKeyDown, handleClickOutside]);
|
|
150
|
+
const removeListeners = (0, _react.useCallback)(() => {
|
|
151
|
+
/* istanbul ignore else */
|
|
152
|
+
if (listening.current) {
|
|
153
|
+
document.removeEventListener("click", handleClickOutside);
|
|
154
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
155
|
+
listening.current = false;
|
|
156
|
+
}
|
|
157
|
+
}, [handleKeyDown, handleClickOutside]);
|
|
158
|
+
(0, _react.useEffect)(() => {
|
|
159
|
+
if (showAdditionalButtons) {
|
|
160
|
+
addListeners();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return () => {
|
|
164
|
+
removeListeners();
|
|
165
|
+
};
|
|
166
|
+
}, [showAdditionalButtons, addListeners, removeListeners]);
|
|
147
167
|
|
|
148
168
|
const handleMainButtonKeyDown = ev => {
|
|
149
|
-
if (_events.default.isEnterKey(ev) || _events.default.isSpaceKey(ev)) {
|
|
150
|
-
|
|
169
|
+
if (_events.default.isEnterKey(ev) || _events.default.isSpaceKey(ev) || _events.default.isDownKey(ev)) {
|
|
170
|
+
ev.preventDefault();
|
|
171
|
+
|
|
172
|
+
if (!showAdditionalButtons) {
|
|
173
|
+
showButtons();
|
|
174
|
+
} // see if setTimeout could be removed after we update react to v18 thanks to the concurrent mode
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
setTimeout(() => {
|
|
178
|
+
var _additionalButtons$cu, _additionalButtons$cu2;
|
|
179
|
+
|
|
180
|
+
(_additionalButtons$cu = additionalButtons.current[0]) === null || _additionalButtons$cu === void 0 ? void 0 : (_additionalButtons$cu2 = _additionalButtons$cu.current) === null || _additionalButtons$cu2 === void 0 ? void 0 : _additionalButtons$cu2.focus();
|
|
181
|
+
}, 0);
|
|
151
182
|
}
|
|
152
183
|
};
|
|
153
184
|
|
|
154
185
|
const focusMainButton = () => {
|
|
155
186
|
isMainButtonFocused.current = true;
|
|
187
|
+
|
|
188
|
+
if (isFocusedAfterClosing.current) {
|
|
189
|
+
isFocusedAfterClosing.current = false;
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
156
193
|
showButtons();
|
|
157
194
|
};
|
|
158
195
|
|
|
196
|
+
const blurMainButton = () => {
|
|
197
|
+
isMainButtonFocused.current = false;
|
|
198
|
+
};
|
|
199
|
+
|
|
159
200
|
const mainButtonProps = () => {
|
|
160
201
|
const opts = {
|
|
161
202
|
disabled,
|
|
162
203
|
displayed: showAdditionalButtons,
|
|
163
204
|
onTouchStart: showButtons,
|
|
164
205
|
onFocus: focusMainButton,
|
|
165
|
-
onBlur:
|
|
166
|
-
isMainButtonFocused.current = false;
|
|
167
|
-
},
|
|
206
|
+
onBlur: blurMainButton,
|
|
168
207
|
onKeyDown: handleMainButtonKeyDown,
|
|
169
208
|
buttonType: buttonType || as,
|
|
170
209
|
size,
|
|
@@ -191,23 +230,6 @@ const MultiActionButton = ({
|
|
|
191
230
|
ref: buttonContainer
|
|
192
231
|
}, childrenWithProps()));
|
|
193
232
|
|
|
194
|
-
(0, _react.useEffect)(() => {
|
|
195
|
-
if (removeHandleClickOutside) {
|
|
196
|
-
setRemoveHandleClickOutside(false);
|
|
197
|
-
document.removeEventListener(userInputType, handleClickOutside);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (removeHandleKeyDown) {
|
|
201
|
-
setRemoveHandleKeyDown(false);
|
|
202
|
-
document.removeEventListener("keydown", handleKeyDown);
|
|
203
|
-
}
|
|
204
|
-
}, [handleClickOutside, handleKeyDown, removeHandleClickOutside, removeHandleKeyDown, userInputType]);
|
|
205
|
-
(0, _react.useEffect)(() => {
|
|
206
|
-
return () => {
|
|
207
|
-
document.removeEventListener(userInputType, handleClickOutside);
|
|
208
|
-
document.removeEventListener("keydown", handleKeyDown);
|
|
209
|
-
};
|
|
210
|
-
}, [handleClickOutside, handleKeyDown, userInputType]);
|
|
211
233
|
return /*#__PURE__*/_react.default.createElement(_multiActionButton.StyledMultiActionButton, _extends({
|
|
212
234
|
"aria-haspopup": "true",
|
|
213
235
|
onMouseLeave: hideButtons,
|
|
@@ -36,9 +36,15 @@ const StyledMultiActionButton = _styledComponents.default.div`
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
&:focus {
|
|
39
|
+
background-color: var(--colorsActionMajor700);
|
|
39
40
|
border: 3px solid var(--colorsSemanticFocus500);
|
|
40
41
|
outline: none;
|
|
41
|
-
margin: -1px;
|
|
42
|
+
margin: 0 -1px;
|
|
43
|
+
|
|
44
|
+
&,
|
|
45
|
+
${_icon.default} {
|
|
46
|
+
color: var(--colorsActionMajorYang100);
|
|
47
|
+
}
|
|
42
48
|
}
|
|
43
49
|
|
|
44
50
|
${({
|
|
@@ -69,6 +69,15 @@ const StyledSplitButtonToggle = (0, _styledComponents.default)(_button.default)`
|
|
|
69
69
|
${_button.default} + & ${_icon.default} {
|
|
70
70
|
margin-left: 0;
|
|
71
71
|
}
|
|
72
|
+
|
|
73
|
+
&:focus {
|
|
74
|
+
background-color: var(--colorsActionMajor500);
|
|
75
|
+
|
|
76
|
+
&,
|
|
77
|
+
${_icon.default} {
|
|
78
|
+
color: var(--colorsActionMajorYang100);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
72
81
|
`}
|
|
73
82
|
`;
|
|
74
83
|
var _default = StyledSplitButtonToggle;
|
|
@@ -75,14 +75,18 @@ const SplitButton = ({
|
|
|
75
75
|
const theme = (0, _react.useContext)(_styledComponents.ThemeContext) || _themes.baseTheme;
|
|
76
76
|
|
|
77
77
|
const isToggleButtonFocused = (0, _react.useRef)(false);
|
|
78
|
+
const isFocusedAfterClosing = (0, _react.useRef)(false);
|
|
78
79
|
const buttonLabelId = (0, _react.useRef)((0, _guid.default)());
|
|
79
|
-
|
|
80
|
+
|
|
81
|
+
const buttonChildren = _react.default.Children.toArray(children);
|
|
82
|
+
|
|
83
|
+
const additionalButtons = (0, _react.useRef)(buttonChildren.map(() => /*#__PURE__*/_react.default.createRef()));
|
|
80
84
|
const splitButtonNode = (0, _react.useRef)();
|
|
81
85
|
const toggleButton = (0, _react.useRef)();
|
|
82
86
|
const buttonContainer = (0, _react.useRef)();
|
|
83
87
|
const [showAdditionalButtons, setShowAdditionalButtons] = (0, _react.useState)(false);
|
|
84
88
|
const [minWidth, setMinWidth] = (0, _react.useState)(0);
|
|
85
|
-
const
|
|
89
|
+
const listening = (0, _react.useRef)(false);
|
|
86
90
|
const hideButtons = (0, _react.useCallback)(() => {
|
|
87
91
|
if (isToggleButtonFocused.current) return;
|
|
88
92
|
setShowAdditionalButtons(false);
|
|
@@ -95,12 +99,8 @@ const SplitButton = ({
|
|
|
95
99
|
}
|
|
96
100
|
}, [hideButtons]);
|
|
97
101
|
const handleKeyDown = (0, _react.useCallback)(ev => {
|
|
98
|
-
if (!showAdditionalButtons) {
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
102
|
const numOfChildren = children.length - 1;
|
|
103
|
-
const currentIndex = additionalButtons.current.findIndex(
|
|
103
|
+
const currentIndex = additionalButtons.current.findIndex(node => node.current === document.activeElement);
|
|
104
104
|
let nextIndex = -1;
|
|
105
105
|
|
|
106
106
|
if (_events.default.isUpKey(ev)) {
|
|
@@ -111,7 +111,9 @@ const SplitButton = ({
|
|
|
111
111
|
if (_events.default.isDownKey(ev)) {
|
|
112
112
|
nextIndex = currentIndex < numOfChildren ? currentIndex + 1 : 0;
|
|
113
113
|
ev.preventDefault();
|
|
114
|
-
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (_events.default.isTabKey(ev)) {
|
|
115
117
|
var _elements$indexOf;
|
|
116
118
|
|
|
117
119
|
const elements = Array.from(document.querySelectorAll(_focusTrapUtils.defaultFocusableSelectors)).filter(el => Number(el.tabIndex) !== -1);
|
|
@@ -122,18 +124,34 @@ const SplitButton = ({
|
|
|
122
124
|
}
|
|
123
125
|
|
|
124
126
|
if (nextIndex > -1) {
|
|
125
|
-
additionalButtons.current[nextIndex].focus();
|
|
127
|
+
additionalButtons.current[nextIndex].current.focus();
|
|
126
128
|
}
|
|
127
|
-
}, [hideButtons, children
|
|
128
|
-
(0, _react.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
129
|
+
}, [hideButtons, children]);
|
|
130
|
+
const addListeners = (0, _react.useCallback)(() => {
|
|
131
|
+
/* istanbul ignore else */
|
|
132
|
+
if (!listening.current) {
|
|
133
|
+
document.addEventListener("click", handleClickOutside);
|
|
134
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
135
|
+
listening.current = true;
|
|
136
|
+
}
|
|
137
|
+
}, [handleKeyDown, handleClickOutside]);
|
|
138
|
+
const removeListeners = (0, _react.useCallback)(() => {
|
|
139
|
+
/* istanbul ignore else */
|
|
140
|
+
if (listening.current) {
|
|
141
|
+
document.removeEventListener("click", handleClickOutside);
|
|
134
142
|
document.removeEventListener("keydown", handleKeyDown);
|
|
143
|
+
listening.current = false;
|
|
144
|
+
}
|
|
145
|
+
}, [handleKeyDown, handleClickOutside]);
|
|
146
|
+
(0, _react.useEffect)(() => {
|
|
147
|
+
if (showAdditionalButtons) {
|
|
148
|
+
addListeners();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return () => {
|
|
152
|
+
removeListeners();
|
|
135
153
|
};
|
|
136
|
-
}, [
|
|
154
|
+
}, [showAdditionalButtons, addListeners, removeListeners]);
|
|
137
155
|
|
|
138
156
|
function mainButtonProps() {
|
|
139
157
|
return {
|
|
@@ -181,11 +199,6 @@ const SplitButton = ({
|
|
|
181
199
|
};
|
|
182
200
|
}
|
|
183
201
|
|
|
184
|
-
function addRef(ref, index) {
|
|
185
|
-
if (!ref) return;
|
|
186
|
-
additionalButtons.current[index] = ref;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
202
|
function getIconColor() {
|
|
190
203
|
const colorsMap = {
|
|
191
204
|
primary: theme.colors.white,
|
|
@@ -222,8 +235,18 @@ const SplitButton = ({
|
|
|
222
235
|
}
|
|
223
236
|
|
|
224
237
|
function handleToggleButtonKeyDown(ev) {
|
|
225
|
-
if (_events.default.isEnterKey(ev) || _events.default.isSpaceKey(ev)) {
|
|
226
|
-
|
|
238
|
+
if (_events.default.isEnterKey(ev) || _events.default.isSpaceKey(ev) || _events.default.isDownKey(ev)) {
|
|
239
|
+
ev.preventDefault();
|
|
240
|
+
|
|
241
|
+
if (!showAdditionalButtons) {
|
|
242
|
+
showButtons();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
setTimeout(() => {
|
|
246
|
+
var _additionalButtons$cu, _additionalButtons$cu2;
|
|
247
|
+
|
|
248
|
+
(_additionalButtons$cu = additionalButtons.current[0]) === null || _additionalButtons$cu === void 0 ? void 0 : (_additionalButtons$cu2 = _additionalButtons$cu.current) === null || _additionalButtons$cu2 === void 0 ? void 0 : _additionalButtons$cu2.focus();
|
|
249
|
+
}, 0);
|
|
227
250
|
}
|
|
228
251
|
}
|
|
229
252
|
|
|
@@ -233,8 +256,17 @@ const SplitButton = ({
|
|
|
233
256
|
const childProps = {
|
|
234
257
|
key: index.toString(),
|
|
235
258
|
role: "menuitem",
|
|
236
|
-
ref:
|
|
237
|
-
tabIndex: -1
|
|
259
|
+
ref: additionalButtons.current[index],
|
|
260
|
+
tabIndex: -1,
|
|
261
|
+
onClick: ev => {
|
|
262
|
+
var _toggleButton$current;
|
|
263
|
+
|
|
264
|
+
if (child.props.onClick) child.props.onClick(ev);
|
|
265
|
+
isToggleButtonFocused.current = false;
|
|
266
|
+
hideButtons();
|
|
267
|
+
isFocusedAfterClosing.current = true;
|
|
268
|
+
(_toggleButton$current = toggleButton.current) === null || _toggleButton$current === void 0 ? void 0 : _toggleButton$current.focus();
|
|
269
|
+
}
|
|
238
270
|
};
|
|
239
271
|
|
|
240
272
|
if (child.type === _button.default) {
|
|
@@ -247,11 +279,13 @@ const SplitButton = ({
|
|
|
247
279
|
|
|
248
280
|
function focusToggleButton() {
|
|
249
281
|
isToggleButtonFocused.current = true;
|
|
250
|
-
showButtons();
|
|
251
|
-
}
|
|
252
282
|
|
|
253
|
-
|
|
254
|
-
|
|
283
|
+
if (isFocusedAfterClosing.current) {
|
|
284
|
+
isFocusedAfterClosing.current = false;
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
showButtons();
|
|
255
289
|
}
|
|
256
290
|
|
|
257
291
|
function renderAdditionalButtons() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "carbon-react",
|
|
3
|
-
"version": "106.
|
|
3
|
+
"version": "106.6.0",
|
|
4
4
|
"description": "A library of reusable React components for easily building user interfaces.",
|
|
5
5
|
"engineStrict": true,
|
|
6
6
|
"engines": {
|
|
@@ -164,7 +164,7 @@
|
|
|
164
164
|
},
|
|
165
165
|
"dependencies": {
|
|
166
166
|
"@octokit/rest": "^18.12.0",
|
|
167
|
-
"@popperjs/core": "^2.11.
|
|
167
|
+
"@popperjs/core": "^2.11.5",
|
|
168
168
|
"@styled-system/prop-types": "^5.1.5",
|
|
169
169
|
"@tippyjs/react": "^4.2.5",
|
|
170
170
|
"@types/styled-system": "^5.1.11",
|