carbon-react 119.9.1 → 119.9.3

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.
Files changed (28) hide show
  1. package/esm/components/button/button.component.js +8 -1
  2. package/esm/components/multi-action-button/multi-action-button.component.js +21 -68
  3. package/esm/components/portrait/portrait.component.d.ts +5 -12
  4. package/esm/components/portrait/portrait.component.js +2 -0
  5. package/esm/components/split-button/__internal__/split-button.context.d.ts +7 -0
  6. package/esm/components/split-button/__internal__/split-button.context.js +4 -0
  7. package/esm/components/split-button/split-button.component.js +20 -61
  8. package/esm/hooks/__internal__/useChildButtons/index.d.ts +1 -0
  9. package/esm/hooks/__internal__/useChildButtons/index.js +1 -0
  10. package/esm/hooks/__internal__/useChildButtons/useChildButtons.d.ts +21 -0
  11. package/esm/hooks/__internal__/useChildButtons/useChildButtons.js +71 -0
  12. package/esm/hooks/__internal__/useMenuKeyboardNavigation/useMenuKeyboardNavigation.d.ts +1 -1
  13. package/esm/hooks/__internal__/useMenuKeyboardNavigation/useMenuKeyboardNavigation.js +8 -7
  14. package/lib/components/button/button.component.js +8 -1
  15. package/lib/components/multi-action-button/multi-action-button.component.js +20 -67
  16. package/lib/components/portrait/portrait.component.d.ts +5 -12
  17. package/lib/components/portrait/portrait.component.js +2 -0
  18. package/lib/components/split-button/__internal__/split-button.context.d.ts +7 -0
  19. package/lib/components/split-button/__internal__/split-button.context.js +12 -0
  20. package/lib/components/split-button/split-button.component.js +19 -60
  21. package/lib/hooks/__internal__/useChildButtons/index.d.ts +1 -0
  22. package/lib/hooks/__internal__/useChildButtons/index.js +13 -0
  23. package/lib/hooks/__internal__/useChildButtons/package.json +6 -0
  24. package/lib/hooks/__internal__/useChildButtons/useChildButtons.d.ts +21 -0
  25. package/lib/hooks/__internal__/useChildButtons/useChildButtons.js +79 -0
  26. package/lib/hooks/__internal__/useMenuKeyboardNavigation/useMenuKeyboardNavigation.d.ts +1 -1
  27. package/lib/hooks/__internal__/useMenuKeyboardNavigation/useMenuKeyboardNavigation.js +7 -6
  28. package/package.json +1 -1
@@ -8,6 +8,7 @@ import tagComponent from "../../__internal__/utils/helpers/tags/tags";
8
8
  import { TooltipProvider } from "../../__internal__/tooltip-provider";
9
9
  import Logger from "../../__internal__/utils/logger";
10
10
  import { ButtonBarContext } from "../button-bar/button-bar.component";
11
+ import SplitButtonContext from "../split-button/__internal__/split-button.context";
11
12
  function renderChildren(_ref) {
12
13
  let {
13
14
  /* eslint-disable react/prop-types */
@@ -79,6 +80,7 @@ const Button = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
79
80
  iconTooltipMessage,
80
81
  iconTooltipPosition,
81
82
  fullWidth: fullWidthProp = false,
83
+ onClick,
82
84
  ...rest
83
85
  } = _ref2;
84
86
  const {
@@ -104,6 +106,10 @@ const Button = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
104
106
  Logger.deprecate("The `dashed` variant of the `buttonType` prop for `Button` component is deprecated and will soon be removed.");
105
107
  }
106
108
  const [internalRef, setInternalRef] = useState();
109
+ const {
110
+ inSplitButton,
111
+ onChildButtonClick
112
+ } = useContext(SplitButtonContext);
107
113
  let paddingX;
108
114
  const handleLinkKeyDown = event => {
109
115
  // If space key click link
@@ -134,11 +140,12 @@ const Button = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
134
140
  "aria-label": !isValidChildren && iconType ? ariaLabel || iconType : ariaLabel,
135
141
  as: !disabled && href ? "a" : "button",
136
142
  onKeyDown: href ? handleLinkKeyDown : undefined,
143
+ onClick: inSplitButton ? onChildButtonClick?.(onClick) : onClick,
137
144
  draggable: false,
138
145
  buttonType: buttonType,
139
146
  disabled: disabled,
140
147
  destructive: destructive,
141
- role: "button",
148
+ role: inSplitButton ? "menu-item" : "button",
142
149
  type: href ? undefined : "button",
143
150
  iconType: iconType,
144
151
  size: size,
@@ -1,13 +1,13 @@
1
1
  function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
- import React, { useCallback, useState, useRef, useMemo } from "react";
2
+ import React, { useRef } from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import useClickAwayListener from "../../hooks/__internal__/useClickAwayListener";
5
+ import SplitButtonContext from "../split-button/__internal__/split-button.context";
5
6
  import { StyledMultiActionButton, StyledButtonChildrenContainer } from "./multi-action-button.style";
6
7
  import Button from "../button";
7
- import Events from "../../__internal__/utils/helpers/events";
8
8
  import Popover from "../../__internal__/popover";
9
9
  import { filterStyledSystemMarginProps, filterOutStyledSystemSpacingProps } from "../../style/utils";
10
- import useMenuKeyboardNavigation from "../../hooks/__internal__/useMenuKeyboardNavigation";
10
+ import useChildButtons from "../../hooks/__internal__/useChildButtons";
11
11
  export const MultiActionButton = _ref => {
12
12
  let {
13
13
  align = "left",
@@ -23,57 +23,17 @@ export const MultiActionButton = _ref => {
23
23
  "data-role": dataRole,
24
24
  ...rest
25
25
  } = _ref;
26
- const ref = useRef(null);
27
26
  const buttonRef = useRef(null);
28
- const buttonContainer = useRef(null);
29
- const buttonChildren = useMemo(() => React.Children.toArray(children), [children]);
30
- const buttonChildrenRefs = useMemo(() => buttonChildren.map(() => /*#__PURE__*/React.createRef()), [buttonChildren]);
31
- const [showAdditionalButtons, setShowAdditionalButtons] = useState(false);
32
- const [minWidth, setMinWidth] = useState(0);
33
- const hideButtons = useCallback(() => {
34
- setShowAdditionalButtons(false);
35
- }, []);
36
- const showButtons = () => {
37
- setShowAdditionalButtons(true);
38
-
39
- /* istanbul ignore else */
40
- if (ref.current) {
41
- setMinWidth(ref.current.getBoundingClientRect().width);
42
- }
43
- };
44
- const childrenWithProps = () => {
45
- return buttonChildren.map((child, index) => {
46
- if (! /*#__PURE__*/React.isValidElement(child)) {
47
- return child;
48
- }
49
- const props = {
50
- key: index.toString(),
51
- role: "menuitem",
52
- ref: buttonChildrenRefs[index],
53
- tabIndex: -1,
54
- onClick: ev => {
55
- if (child.props.onClick) child.props.onClick(ev);
56
- hideButtons();
57
- buttonRef.current?.focus();
58
- }
59
- };
60
- return /*#__PURE__*/React.cloneElement(child, props);
61
- });
62
- };
63
- const handleKeyDown = useMenuKeyboardNavigation(buttonRef, buttonChildrenRefs, hideButtons, showAdditionalButtons);
64
- const handleMainButtonKeyDown = ev => {
65
- if (Events.isEnterKey(ev) || Events.isSpaceKey(ev) || Events.isDownKey(ev) || Events.isUpKey(ev)) {
66
- ev.preventDefault();
67
- if (!showAdditionalButtons) {
68
- showButtons();
69
- }
70
-
71
- // see if setTimeout could be removed after we update react to v18 thanks to the concurrent mode
72
- setTimeout(() => {
73
- buttonChildrenRefs[0]?.current?.focus();
74
- }, 0);
75
- }
76
- };
27
+ const {
28
+ showAdditionalButtons,
29
+ showButtons,
30
+ hideButtons,
31
+ buttonNode,
32
+ hideButtonsIfTriggerNotFocused,
33
+ handleToggleButtonKeyDown,
34
+ wrapperProps,
35
+ contextValue
36
+ } = useChildButtons(buttonRef);
77
37
  const handleInsideClick = useClickAwayListener(hideButtons);
78
38
  const handleClick = ev => {
79
39
  showButtons();
@@ -86,7 +46,7 @@ export const MultiActionButton = _ref => {
86
46
  disabled,
87
47
  displayed: showAdditionalButtons,
88
48
  onTouchStart: showButtons,
89
- onKeyDown: handleMainButtonKeyDown,
49
+ onKeyDown: handleToggleButtonKeyDown,
90
50
  onClick: handleClick,
91
51
  buttonType,
92
52
  size,
@@ -98,24 +58,17 @@ export const MultiActionButton = _ref => {
98
58
  };
99
59
  const renderAdditionalButtons = () => /*#__PURE__*/React.createElement(Popover, {
100
60
  placement: "bottom-end",
101
- reference: ref
102
- }, /*#__PURE__*/React.createElement(StyledButtonChildrenContainer, {
103
- role: "menu",
61
+ reference: buttonNode
62
+ }, /*#__PURE__*/React.createElement(StyledButtonChildrenContainer, _extends({}, wrapperProps, {
104
63
  "aria-label": text,
105
- "data-element": "additional-buttons",
106
- align: align,
107
- minWidth: minWidth,
108
- ref: buttonContainer,
109
- onKeyDown: handleKeyDown
110
- }, childrenWithProps()));
111
- const hideButtonsIfTriggerNotFocused = useCallback(() => {
112
- if (buttonRef.current === document.activeElement) return;
113
- setShowAdditionalButtons(false);
114
- }, []);
64
+ align: align
65
+ }), /*#__PURE__*/React.createElement(SplitButtonContext.Provider, {
66
+ value: contextValue
67
+ }, children)));
115
68
  const marginProps = filterStyledSystemMarginProps(rest);
116
69
  return /*#__PURE__*/React.createElement(StyledMultiActionButton, _extends({
117
70
  onMouseLeave: hideButtonsIfTriggerNotFocused,
118
- ref: ref,
71
+ ref: buttonNode,
119
72
  "data-component": "multi-action-button",
120
73
  "data-element": dataElement,
121
74
  "data-role": dataRole,
@@ -3,7 +3,11 @@ import { MarginProps } from "styled-system";
3
3
  import { IconType } from "../icon";
4
4
  export declare type PortraitShapes = "circle" | "square";
5
5
  export declare type PortraitSizes = "XS" | "S" | "M" | "ML" | "L" | "XL" | "XXL";
6
- export interface PortraitBaseProps extends MarginProps {
6
+ export interface PortraitProps extends MarginProps {
7
+ /** An email address registered with Gravatar. */
8
+ gravatar?: string;
9
+ /** A custom image URL. */
10
+ src?: string;
7
11
  /** The size of the Portrait. */
8
12
  size?: PortraitSizes;
9
13
  /** The `alt` HTML string. */
@@ -35,16 +39,5 @@ export interface PortraitBaseProps extends MarginProps {
35
39
  /** Override font color of the Tooltip, provide any color from palette or any valid css color value. */
36
40
  tooltipFontColor?: string;
37
41
  }
38
- export interface PortraitWithGravatar extends PortraitBaseProps {
39
- /** An email address registered with Gravatar. */
40
- gravatar?: string;
41
- src?: never;
42
- }
43
- export interface PortraitWithSrc extends PortraitBaseProps {
44
- /** A custom image URL. */
45
- src?: string;
46
- gravatar?: never;
47
- }
48
- export declare type PortraitProps = PortraitWithGravatar | PortraitWithSrc;
49
42
  export declare const Portrait: ({ alt, darkBackground, gravatar, iconType, initials, shape, size, src, onClick, tooltipMessage, tooltipId, tooltipIsVisible, tooltipPosition, tooltipType, tooltipSize, tooltipBgColor, tooltipFontColor, ...rest }: PortraitProps) => React.JSX.Element;
50
43
  export default Portrait;
@@ -1,6 +1,7 @@
1
1
  function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
2
  import React, { useEffect, useState, useContext } from "react";
3
3
  import PropTypes from "prop-types";
4
+ import invariant from "invariant";
4
5
  import Tooltip from "../tooltip";
5
6
  import tagComponent from "../../__internal__/utils/helpers/tags/tags";
6
7
  import PortraitGravatar from "./portrait-gravatar.component";
@@ -34,6 +35,7 @@ export const Portrait = _ref => {
34
35
  roundedCornersOptOut
35
36
  } = useContext(RoundedCornersOptOutContext);
36
37
  const defaultShape = roundedCornersOptOut ? "square" : "circle";
38
+ !!(src && gravatar) ? process.env.NODE_ENV !== "production" ? invariant(false, "The `src` prop cannot be used in conjunction with the `gravatar` prop." + " Please use one or the other.") : invariant(false) : void 0;
37
39
  useEffect(() => {
38
40
  setExternalError(false);
39
41
  }, [gravatar, src]);
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ export interface SplitButtonContextProps {
3
+ inSplitButton: boolean;
4
+ onChildButtonClick?: (childOnClick?: React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement>) => React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement> | undefined;
5
+ }
6
+ declare const _default: React.Context<SplitButtonContextProps>;
7
+ export default _default;
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ export default /*#__PURE__*/React.createContext({
3
+ inSplitButton: false
4
+ });
@@ -1,5 +1,5 @@
1
1
  function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
- import React, { useContext, useCallback, useMemo, useRef, useState } from "react";
2
+ import React, { useContext, useRef } from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import { ThemeContext } from "styled-components";
5
5
  import useClickAwayListener from "../../hooks/__internal__/useClickAwayListener";
@@ -8,12 +8,12 @@ import Button from "../button";
8
8
  import StyledSplitButton from "./split-button.style";
9
9
  import StyledSplitButtonToggle from "./split-button-toggle.style";
10
10
  import StyledSplitButtonChildrenContainer from "./split-button-children.style";
11
- import Events from "../../__internal__/utils/helpers/events";
12
11
  import guid from "../../__internal__/utils/helpers/guid";
13
12
  import Popover from "../../__internal__/popover";
14
13
  import { filterStyledSystemMarginProps, filterOutStyledSystemSpacingProps } from "../../style/utils";
15
14
  import { baseTheme } from "../../style/themes";
16
- import useMenuKeyboardNavigation from "../../hooks/__internal__/useMenuKeyboardNavigation";
15
+ import useChildButtons from "../../hooks/__internal__/useChildButtons";
16
+ import SplitButtonContext from "./__internal__/split-button.context";
17
17
  const CONTENT_WIDTH_RATIO = 0.75;
18
18
  export const SplitButton = _ref => {
19
19
  let {
@@ -33,39 +33,17 @@ export const SplitButton = _ref => {
33
33
  } = _ref;
34
34
  const theme = useContext(ThemeContext) || baseTheme;
35
35
  const buttonLabelId = useRef(guid());
36
- const buttonChildren = useMemo(() => React.Children.toArray(children), [children]);
37
- const buttonChildrenRefs = useMemo(() => buttonChildren.map(() => /*#__PURE__*/React.createRef()), [buttonChildren]);
38
- const splitButtonNode = useRef(null);
39
36
  const toggleButton = useRef(null);
40
- const [showAdditionalButtons, setShowAdditionalButtons] = useState(false);
41
- const [minWidth, setMinWidth] = useState(0);
42
- const hideButtons = useCallback(() => {
43
- setShowAdditionalButtons(false);
44
- }, []);
45
- const handleKeyDown = useMenuKeyboardNavigation(toggleButton, buttonChildrenRefs, hideButtons, showAdditionalButtons);
46
- function showButtons() {
47
- setShowAdditionalButtons(true);
48
-
49
- /* istanbul ignore else */
50
- if (splitButtonNode.current) {
51
- setMinWidth(CONTENT_WIDTH_RATIO * splitButtonNode.current.getBoundingClientRect().width);
52
- }
53
- }
54
- function handleToggleButtonKeyDown(ev) {
55
- if (Events.isEnterKey(ev) || Events.isSpaceKey(ev) || Events.isDownKey(ev) || Events.isUpKey(ev)) {
56
- ev.preventDefault();
57
- if (!showAdditionalButtons) {
58
- showButtons();
59
- }
60
- setTimeout(() => {
61
- buttonChildrenRefs[0]?.current?.focus();
62
- }, 0);
63
- }
64
- }
65
- const hideButtonsIfTriggerNotFocused = useCallback(() => {
66
- if (toggleButton.current === document.activeElement) return;
67
- setShowAdditionalButtons(false);
68
- }, []);
37
+ const {
38
+ showAdditionalButtons,
39
+ showButtons,
40
+ hideButtons,
41
+ buttonNode,
42
+ hideButtonsIfTriggerNotFocused,
43
+ handleToggleButtonKeyDown,
44
+ wrapperProps,
45
+ contextValue
46
+ } = useChildButtons(toggleButton, CONTENT_WIDTH_RATIO);
69
47
  const mainButtonProps = {
70
48
  onMouseEnter: hideButtonsIfTriggerNotFocused,
71
49
  onFocus: hideButtonsIfTriggerNotFocused,
@@ -126,43 +104,24 @@ export const SplitButton = _ref => {
126
104
  disabled: disabled
127
105
  }))];
128
106
  }
129
- function childrenWithProps() {
130
- const childArray = Array.isArray(children) ? children : [children];
131
- return childArray.filter(Boolean).map((child, index) => {
132
- const childProps = {
133
- key: index.toString(),
134
- role: "menuitem",
135
- ref: buttonChildrenRefs[index],
136
- tabIndex: -1,
137
- onClick: ev => {
138
- if (child.props.onClick) child.props.onClick(ev);
139
- hideButtons();
140
- toggleButton.current?.focus();
141
- }
142
- };
143
- return /*#__PURE__*/React.cloneElement(child, childProps);
144
- });
145
- }
146
107
  function renderAdditionalButtons() {
147
108
  if (!showAdditionalButtons) return null;
148
109
  return /*#__PURE__*/React.createElement(Popover, {
149
110
  placement: "bottom-end",
150
- reference: splitButtonNode
151
- }, /*#__PURE__*/React.createElement(StyledSplitButtonChildrenContainer, {
152
- role: "menu",
111
+ reference: buttonNode
112
+ }, /*#__PURE__*/React.createElement(StyledSplitButtonChildrenContainer, _extends({}, wrapperProps, {
153
113
  "aria-label": text,
154
- "data-element": "additional-buttons",
155
- align: align,
156
- minWidth: minWidth,
157
- onKeyDown: handleKeyDown
158
- }, childrenWithProps()));
114
+ align: align
115
+ }), /*#__PURE__*/React.createElement(SplitButtonContext.Provider, {
116
+ value: contextValue
117
+ }, children)));
159
118
  }
160
119
  const handleClick = useClickAwayListener(hideButtons);
161
120
  const marginProps = filterStyledSystemMarginProps(rest);
162
121
  return /*#__PURE__*/React.createElement(StyledSplitButton, _extends({
163
122
  onMouseLeave: hideButtonsIfTriggerNotFocused,
164
123
  onClick: handleClick,
165
- ref: splitButtonNode
124
+ ref: buttonNode
166
125
  }, componentTags(), marginProps), renderMainButton(), renderAdditionalButtons());
167
126
  };
168
127
  export default SplitButton;
@@ -0,0 +1 @@
1
+ export { default } from "./useChildButtons";
@@ -0,0 +1 @@
1
+ export { default } from "./useChildButtons";
@@ -0,0 +1,21 @@
1
+ /// <reference types="react" />
2
+ declare const useChildButtons: (toggleButtonRef: React.RefObject<HTMLButtonElement>, widthRatio?: number) => {
3
+ showAdditionalButtons: boolean;
4
+ showButtons: () => void;
5
+ hideButtons: () => void;
6
+ buttonNode: import("react").RefObject<HTMLDivElement>;
7
+ hideButtonsIfTriggerNotFocused: () => void;
8
+ handleToggleButtonKeyDown: (ev: React.KeyboardEvent<HTMLButtonElement>) => void;
9
+ wrapperProps: {
10
+ role: string;
11
+ "data-element": string;
12
+ onKeyDown: (ev: any) => void;
13
+ minWidth: number;
14
+ ref: import("react").RefObject<HTMLDivElement>;
15
+ };
16
+ contextValue: {
17
+ inSplitButton: boolean;
18
+ onChildButtonClick: (childOnClick?: React.MouseEventHandler<HTMLButtonElement>) => (ev: React.MouseEvent<HTMLButtonElement>) => void;
19
+ };
20
+ };
21
+ export default useChildButtons;
@@ -0,0 +1,71 @@
1
+ import { useState, useRef, useEffect, useCallback } from "react";
2
+ import Events from "../../../__internal__/utils/helpers/events";
3
+ import useMenuKeyboardNavigation from "../useMenuKeyboardNavigation";
4
+ const useChildButtons = function (toggleButtonRef) {
5
+ let widthRatio = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
6
+ const [showAdditionalButtons, setShowAdditionalButtons] = useState(false);
7
+ const [minWidth, setMinWidth] = useState(0);
8
+ const buttonNode = useRef(null);
9
+ const childrenContainer = useRef(null);
10
+ const focusFirstChildButtonOnOpen = useRef(false);
11
+ const hideButtons = useCallback(() => {
12
+ setShowAdditionalButtons(false);
13
+ }, []);
14
+ function showButtons() {
15
+ setShowAdditionalButtons(true);
16
+
17
+ /* istanbul ignore else */
18
+ if (buttonNode.current) {
19
+ setMinWidth(widthRatio * buttonNode.current.getBoundingClientRect().width);
20
+ }
21
+ }
22
+ const getButtonChildren = useCallback(() => childrenContainer.current?.querySelectorAll('[data-component="button"]'), []);
23
+ useEffect(() => {
24
+ const firstChild = getButtonChildren()?.[0];
25
+ if (focusFirstChildButtonOnOpen.current && showAdditionalButtons && firstChild) {
26
+ focusFirstChildButtonOnOpen.current = false;
27
+ firstChild.focus();
28
+ }
29
+ }, [showAdditionalButtons, getButtonChildren]);
30
+ const handleToggleButtonKeyDown = ev => {
31
+ if (Events.isEnterKey(ev) || Events.isSpaceKey(ev) || Events.isDownKey(ev) || Events.isUpKey(ev)) {
32
+ ev.preventDefault();
33
+ if (!showAdditionalButtons) {
34
+ showButtons();
35
+ }
36
+ focusFirstChildButtonOnOpen.current = true;
37
+ }
38
+ };
39
+ const handleKeyDown = useMenuKeyboardNavigation(toggleButtonRef, getButtonChildren, hideButtons, showAdditionalButtons);
40
+ const onChildButtonClick = childOnClick => ev => {
41
+ childOnClick?.(ev);
42
+ hideButtons();
43
+ toggleButtonRef.current?.focus();
44
+ };
45
+ const hideButtonsIfTriggerNotFocused = useCallback(() => {
46
+ if (toggleButtonRef.current === document.activeElement) return;
47
+ setShowAdditionalButtons(false);
48
+ }, [toggleButtonRef]);
49
+ const wrapperProps = {
50
+ role: "menu",
51
+ "data-element": "additional-buttons",
52
+ onKeyDown: handleKeyDown,
53
+ minWidth,
54
+ ref: childrenContainer
55
+ };
56
+ const contextValue = {
57
+ inSplitButton: true,
58
+ onChildButtonClick
59
+ };
60
+ return {
61
+ showAdditionalButtons,
62
+ showButtons,
63
+ hideButtons,
64
+ buttonNode,
65
+ hideButtonsIfTriggerNotFocused,
66
+ handleToggleButtonKeyDown,
67
+ wrapperProps,
68
+ contextValue
69
+ };
70
+ };
71
+ export default useChildButtons;
@@ -1,2 +1,2 @@
1
- declare const _default: (mainControlRef: React.RefObject<HTMLButtonElement>, childrenRefs: React.RefObject<HTMLButtonElement>[], hide: () => void, isOpen: boolean) => (ev: any) => void;
1
+ declare const _default: (mainControlRef: React.RefObject<HTMLButtonElement>, getButtonChildren: () => NodeListOf<HTMLButtonElement>, hide: () => void, isOpen: boolean) => (ev: any) => void;
2
2
  export default _default;
@@ -1,9 +1,8 @@
1
- import { useCallback, useMemo } from "react";
1
+ import { useCallback } from "react";
2
2
  import Events from "../../../__internal__/utils/helpers/events";
3
3
  import { defaultFocusableSelectors } from "../../../__internal__/focus-trap/focus-trap-utils";
4
4
  import useModalManager from "../useModalManager";
5
- export default ((mainControlRef, childrenRefs, hide, isOpen) => {
6
- const childrenLength = useMemo(() => childrenRefs.length, [childrenRefs]);
5
+ export default ((mainControlRef, getButtonChildren, hide, isOpen) => {
7
6
  const refocusMainControl = useCallback(() => {
8
7
  hide();
9
8
  mainControlRef.current?.focus();
@@ -26,8 +25,10 @@ export default ((mainControlRef, childrenRefs, hide, isOpen) => {
26
25
  if (!(Events.isEnterKey(ev) || Events.isSpaceKey(ev))) {
27
26
  ev.preventDefault();
28
27
  }
29
- const currentIndex = childrenRefs?.findIndex(node => node.current === document.activeElement);
28
+ const buttonChildren = getButtonChildren();
29
+ const childrenLength = buttonChildren?.length;
30
30
  let nextIndex = -1;
31
+ const currentIndex = Array.from(buttonChildren).indexOf(document.activeElement);
31
32
  const arrowModifierPressed = ev.ctrlKey || ev.metaKey;
32
33
  if (Events.isEndKey(ev) || arrowModifierPressed && Events.isDownKey(ev)) {
33
34
  nextIndex = childrenLength - 1;
@@ -54,15 +55,15 @@ export default ((mainControlRef, childrenRefs, hide, isOpen) => {
54
55
  const elements = Array.from(document.querySelectorAll(defaultFocusableSelectors)).filter(el => Number(el.tabIndex) !== -1);
55
56
  const indexOf = elements.indexOf(mainControlRef.current);
56
57
  elements[indexOf + 1]?.focus();
57
- // // timeout enforces that the "hide" method will be run after browser focuses on the next element
58
+ // timeout enforces that the "hide" method will be run after browser focuses on the next element
58
59
  setTimeout(hide, 0);
59
60
  } else {
60
61
  nextIndex = currentIndex + 1;
61
62
  }
62
63
  }
63
64
  if (nextIndex > -1) {
64
- childrenRefs[nextIndex].current?.focus();
65
+ buttonChildren?.[nextIndex]?.focus();
65
66
  }
66
- }, [childrenLength, hide, refocusMainControl, childrenRefs, mainControlRef]);
67
+ }, [hide, refocusMainControl, mainControlRef, getButtonChildren]);
67
68
  return handleKeyDown;
68
69
  });
@@ -13,6 +13,7 @@ var _tags = _interopRequireDefault(require("../../__internal__/utils/helpers/tag
13
13
  var _tooltipProvider = require("../../__internal__/tooltip-provider");
14
14
  var _logger = _interopRequireDefault(require("../../__internal__/utils/logger"));
15
15
  var _buttonBar = require("../button-bar/button-bar.component");
16
+ var _splitButton = _interopRequireDefault(require("../split-button/__internal__/split-button.context"));
16
17
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17
18
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
18
19
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
@@ -88,6 +89,7 @@ const Button = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
88
89
  iconTooltipMessage,
89
90
  iconTooltipPosition,
90
91
  fullWidth: fullWidthProp = false,
92
+ onClick,
91
93
  ...rest
92
94
  } = _ref2;
93
95
  const {
@@ -113,6 +115,10 @@ const Button = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
113
115
  _logger.default.deprecate("The `dashed` variant of the `buttonType` prop for `Button` component is deprecated and will soon be removed.");
114
116
  }
115
117
  const [internalRef, setInternalRef] = (0, _react.useState)();
118
+ const {
119
+ inSplitButton,
120
+ onChildButtonClick
121
+ } = (0, _react.useContext)(_splitButton.default);
116
122
  let paddingX;
117
123
  const handleLinkKeyDown = event => {
118
124
  // If space key click link
@@ -143,11 +149,12 @@ const Button = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
143
149
  "aria-label": !isValidChildren && iconType ? ariaLabel || iconType : ariaLabel,
144
150
  as: !disabled && href ? "a" : "button",
145
151
  onKeyDown: href ? handleLinkKeyDown : undefined,
152
+ onClick: inSplitButton ? onChildButtonClick?.(onClick) : onClick,
146
153
  draggable: false,
147
154
  buttonType: buttonType,
148
155
  disabled: disabled,
149
156
  destructive: destructive,
150
- role: "button",
157
+ role: inSplitButton ? "menu-item" : "button",
151
158
  type: href ? undefined : "button",
152
159
  iconType: iconType,
153
160
  size: size,
@@ -7,12 +7,12 @@ exports.default = exports.MultiActionButton = void 0;
7
7
  var _react = _interopRequireWildcard(require("react"));
8
8
  var _propTypes = _interopRequireDefault(require("prop-types"));
9
9
  var _useClickAwayListener = _interopRequireDefault(require("../../hooks/__internal__/useClickAwayListener"));
10
+ var _splitButton = _interopRequireDefault(require("../split-button/__internal__/split-button.context"));
10
11
  var _multiActionButton = require("./multi-action-button.style");
11
12
  var _button = _interopRequireDefault(require("../button"));
12
- var _events = _interopRequireDefault(require("../../__internal__/utils/helpers/events"));
13
13
  var _popover = _interopRequireDefault(require("../../__internal__/popover"));
14
14
  var _utils = require("../../style/utils");
15
- var _useMenuKeyboardNavigation = _interopRequireDefault(require("../../hooks/__internal__/useMenuKeyboardNavigation"));
15
+ var _useChildButtons = _interopRequireDefault(require("../../hooks/__internal__/useChildButtons"));
16
16
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17
17
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
18
18
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
@@ -32,57 +32,17 @@ const MultiActionButton = _ref => {
32
32
  "data-role": dataRole,
33
33
  ...rest
34
34
  } = _ref;
35
- const ref = (0, _react.useRef)(null);
36
35
  const buttonRef = (0, _react.useRef)(null);
37
- const buttonContainer = (0, _react.useRef)(null);
38
- const buttonChildren = (0, _react.useMemo)(() => _react.default.Children.toArray(children), [children]);
39
- const buttonChildrenRefs = (0, _react.useMemo)(() => buttonChildren.map(() => /*#__PURE__*/_react.default.createRef()), [buttonChildren]);
40
- const [showAdditionalButtons, setShowAdditionalButtons] = (0, _react.useState)(false);
41
- const [minWidth, setMinWidth] = (0, _react.useState)(0);
42
- const hideButtons = (0, _react.useCallback)(() => {
43
- setShowAdditionalButtons(false);
44
- }, []);
45
- const showButtons = () => {
46
- setShowAdditionalButtons(true);
47
-
48
- /* istanbul ignore else */
49
- if (ref.current) {
50
- setMinWidth(ref.current.getBoundingClientRect().width);
51
- }
52
- };
53
- const childrenWithProps = () => {
54
- return buttonChildren.map((child, index) => {
55
- if (! /*#__PURE__*/_react.default.isValidElement(child)) {
56
- return child;
57
- }
58
- const props = {
59
- key: index.toString(),
60
- role: "menuitem",
61
- ref: buttonChildrenRefs[index],
62
- tabIndex: -1,
63
- onClick: ev => {
64
- if (child.props.onClick) child.props.onClick(ev);
65
- hideButtons();
66
- buttonRef.current?.focus();
67
- }
68
- };
69
- return /*#__PURE__*/_react.default.cloneElement(child, props);
70
- });
71
- };
72
- const handleKeyDown = (0, _useMenuKeyboardNavigation.default)(buttonRef, buttonChildrenRefs, hideButtons, showAdditionalButtons);
73
- const handleMainButtonKeyDown = ev => {
74
- if (_events.default.isEnterKey(ev) || _events.default.isSpaceKey(ev) || _events.default.isDownKey(ev) || _events.default.isUpKey(ev)) {
75
- ev.preventDefault();
76
- if (!showAdditionalButtons) {
77
- showButtons();
78
- }
79
-
80
- // see if setTimeout could be removed after we update react to v18 thanks to the concurrent mode
81
- setTimeout(() => {
82
- buttonChildrenRefs[0]?.current?.focus();
83
- }, 0);
84
- }
85
- };
36
+ const {
37
+ showAdditionalButtons,
38
+ showButtons,
39
+ hideButtons,
40
+ buttonNode,
41
+ hideButtonsIfTriggerNotFocused,
42
+ handleToggleButtonKeyDown,
43
+ wrapperProps,
44
+ contextValue
45
+ } = (0, _useChildButtons.default)(buttonRef);
86
46
  const handleInsideClick = (0, _useClickAwayListener.default)(hideButtons);
87
47
  const handleClick = ev => {
88
48
  showButtons();
@@ -95,7 +55,7 @@ const MultiActionButton = _ref => {
95
55
  disabled,
96
56
  displayed: showAdditionalButtons,
97
57
  onTouchStart: showButtons,
98
- onKeyDown: handleMainButtonKeyDown,
58
+ onKeyDown: handleToggleButtonKeyDown,
99
59
  onClick: handleClick,
100
60
  buttonType,
101
61
  size,
@@ -107,24 +67,17 @@ const MultiActionButton = _ref => {
107
67
  };
108
68
  const renderAdditionalButtons = () => /*#__PURE__*/_react.default.createElement(_popover.default, {
109
69
  placement: "bottom-end",
110
- reference: ref
111
- }, /*#__PURE__*/_react.default.createElement(_multiActionButton.StyledButtonChildrenContainer, {
112
- role: "menu",
70
+ reference: buttonNode
71
+ }, /*#__PURE__*/_react.default.createElement(_multiActionButton.StyledButtonChildrenContainer, _extends({}, wrapperProps, {
113
72
  "aria-label": text,
114
- "data-element": "additional-buttons",
115
- align: align,
116
- minWidth: minWidth,
117
- ref: buttonContainer,
118
- onKeyDown: handleKeyDown
119
- }, childrenWithProps()));
120
- const hideButtonsIfTriggerNotFocused = (0, _react.useCallback)(() => {
121
- if (buttonRef.current === document.activeElement) return;
122
- setShowAdditionalButtons(false);
123
- }, []);
73
+ align: align
74
+ }), /*#__PURE__*/_react.default.createElement(_splitButton.default.Provider, {
75
+ value: contextValue
76
+ }, children)));
124
77
  const marginProps = (0, _utils.filterStyledSystemMarginProps)(rest);
125
78
  return /*#__PURE__*/_react.default.createElement(_multiActionButton.StyledMultiActionButton, _extends({
126
79
  onMouseLeave: hideButtonsIfTriggerNotFocused,
127
- ref: ref,
80
+ ref: buttonNode,
128
81
  "data-component": "multi-action-button",
129
82
  "data-element": dataElement,
130
83
  "data-role": dataRole,
@@ -3,7 +3,11 @@ import { MarginProps } from "styled-system";
3
3
  import { IconType } from "../icon";
4
4
  export declare type PortraitShapes = "circle" | "square";
5
5
  export declare type PortraitSizes = "XS" | "S" | "M" | "ML" | "L" | "XL" | "XXL";
6
- export interface PortraitBaseProps extends MarginProps {
6
+ export interface PortraitProps extends MarginProps {
7
+ /** An email address registered with Gravatar. */
8
+ gravatar?: string;
9
+ /** A custom image URL. */
10
+ src?: string;
7
11
  /** The size of the Portrait. */
8
12
  size?: PortraitSizes;
9
13
  /** The `alt` HTML string. */
@@ -35,16 +39,5 @@ export interface PortraitBaseProps extends MarginProps {
35
39
  /** Override font color of the Tooltip, provide any color from palette or any valid css color value. */
36
40
  tooltipFontColor?: string;
37
41
  }
38
- export interface PortraitWithGravatar extends PortraitBaseProps {
39
- /** An email address registered with Gravatar. */
40
- gravatar?: string;
41
- src?: never;
42
- }
43
- export interface PortraitWithSrc extends PortraitBaseProps {
44
- /** A custom image URL. */
45
- src?: string;
46
- gravatar?: never;
47
- }
48
- export declare type PortraitProps = PortraitWithGravatar | PortraitWithSrc;
49
42
  export declare const Portrait: ({ alt, darkBackground, gravatar, iconType, initials, shape, size, src, onClick, tooltipMessage, tooltipId, tooltipIsVisible, tooltipPosition, tooltipType, tooltipSize, tooltipBgColor, tooltipFontColor, ...rest }: PortraitProps) => React.JSX.Element;
50
43
  export default Portrait;
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.default = exports.Portrait = void 0;
7
7
  var _react = _interopRequireWildcard(require("react"));
8
8
  var _propTypes = _interopRequireDefault(require("prop-types"));
9
+ var _invariant = _interopRequireDefault(require("invariant"));
9
10
  var _tooltip = _interopRequireDefault(require("../tooltip"));
10
11
  var _tags = _interopRequireDefault(require("../../__internal__/utils/helpers/tags/tags"));
11
12
  var _portraitGravatar = _interopRequireDefault(require("./portrait-gravatar.component"));
@@ -43,6 +44,7 @@ const Portrait = _ref => {
43
44
  roundedCornersOptOut
44
45
  } = (0, _react.useContext)(_carbonProvider.NewValidationContext);
45
46
  const defaultShape = roundedCornersOptOut ? "square" : "circle";
47
+ !!(src && gravatar) ? process.env.NODE_ENV !== "production" ? (0, _invariant.default)(false, "The `src` prop cannot be used in conjunction with the `gravatar` prop." + " Please use one or the other.") : (0, _invariant.default)(false) : void 0;
46
48
  (0, _react.useEffect)(() => {
47
49
  setExternalError(false);
48
50
  }, [gravatar, src]);
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ export interface SplitButtonContextProps {
3
+ inSplitButton: boolean;
4
+ onChildButtonClick?: (childOnClick?: React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement>) => React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement> | undefined;
5
+ }
6
+ declare const _default: React.Context<SplitButtonContextProps>;
7
+ export default _default;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _react = _interopRequireDefault(require("react"));
8
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
9
+ var _default = /*#__PURE__*/_react.default.createContext({
10
+ inSplitButton: false
11
+ });
12
+ exports.default = _default;
@@ -13,12 +13,12 @@ var _button = _interopRequireDefault(require("../button"));
13
13
  var _splitButton = _interopRequireDefault(require("./split-button.style"));
14
14
  var _splitButtonToggle = _interopRequireDefault(require("./split-button-toggle.style"));
15
15
  var _splitButtonChildren = _interopRequireDefault(require("./split-button-children.style"));
16
- var _events = _interopRequireDefault(require("../../__internal__/utils/helpers/events"));
17
16
  var _guid = _interopRequireDefault(require("../../__internal__/utils/helpers/guid"));
18
17
  var _popover = _interopRequireDefault(require("../../__internal__/popover"));
19
18
  var _utils = require("../../style/utils");
20
19
  var _themes = require("../../style/themes");
21
- var _useMenuKeyboardNavigation = _interopRequireDefault(require("../../hooks/__internal__/useMenuKeyboardNavigation"));
20
+ var _useChildButtons = _interopRequireDefault(require("../../hooks/__internal__/useChildButtons"));
21
+ var _splitButton2 = _interopRequireDefault(require("./__internal__/split-button.context"));
22
22
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
23
23
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
24
24
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
@@ -42,39 +42,17 @@ const SplitButton = _ref => {
42
42
  } = _ref;
43
43
  const theme = (0, _react.useContext)(_styledComponents.ThemeContext) || _themes.baseTheme;
44
44
  const buttonLabelId = (0, _react.useRef)((0, _guid.default)());
45
- const buttonChildren = (0, _react.useMemo)(() => _react.default.Children.toArray(children), [children]);
46
- const buttonChildrenRefs = (0, _react.useMemo)(() => buttonChildren.map(() => /*#__PURE__*/_react.default.createRef()), [buttonChildren]);
47
- const splitButtonNode = (0, _react.useRef)(null);
48
45
  const toggleButton = (0, _react.useRef)(null);
49
- const [showAdditionalButtons, setShowAdditionalButtons] = (0, _react.useState)(false);
50
- const [minWidth, setMinWidth] = (0, _react.useState)(0);
51
- const hideButtons = (0, _react.useCallback)(() => {
52
- setShowAdditionalButtons(false);
53
- }, []);
54
- const handleKeyDown = (0, _useMenuKeyboardNavigation.default)(toggleButton, buttonChildrenRefs, hideButtons, showAdditionalButtons);
55
- function showButtons() {
56
- setShowAdditionalButtons(true);
57
-
58
- /* istanbul ignore else */
59
- if (splitButtonNode.current) {
60
- setMinWidth(CONTENT_WIDTH_RATIO * splitButtonNode.current.getBoundingClientRect().width);
61
- }
62
- }
63
- function handleToggleButtonKeyDown(ev) {
64
- if (_events.default.isEnterKey(ev) || _events.default.isSpaceKey(ev) || _events.default.isDownKey(ev) || _events.default.isUpKey(ev)) {
65
- ev.preventDefault();
66
- if (!showAdditionalButtons) {
67
- showButtons();
68
- }
69
- setTimeout(() => {
70
- buttonChildrenRefs[0]?.current?.focus();
71
- }, 0);
72
- }
73
- }
74
- const hideButtonsIfTriggerNotFocused = (0, _react.useCallback)(() => {
75
- if (toggleButton.current === document.activeElement) return;
76
- setShowAdditionalButtons(false);
77
- }, []);
46
+ const {
47
+ showAdditionalButtons,
48
+ showButtons,
49
+ hideButtons,
50
+ buttonNode,
51
+ hideButtonsIfTriggerNotFocused,
52
+ handleToggleButtonKeyDown,
53
+ wrapperProps,
54
+ contextValue
55
+ } = (0, _useChildButtons.default)(toggleButton, CONTENT_WIDTH_RATIO);
78
56
  const mainButtonProps = {
79
57
  onMouseEnter: hideButtonsIfTriggerNotFocused,
80
58
  onFocus: hideButtonsIfTriggerNotFocused,
@@ -135,43 +113,24 @@ const SplitButton = _ref => {
135
113
  disabled: disabled
136
114
  }))];
137
115
  }
138
- function childrenWithProps() {
139
- const childArray = Array.isArray(children) ? children : [children];
140
- return childArray.filter(Boolean).map((child, index) => {
141
- const childProps = {
142
- key: index.toString(),
143
- role: "menuitem",
144
- ref: buttonChildrenRefs[index],
145
- tabIndex: -1,
146
- onClick: ev => {
147
- if (child.props.onClick) child.props.onClick(ev);
148
- hideButtons();
149
- toggleButton.current?.focus();
150
- }
151
- };
152
- return /*#__PURE__*/_react.default.cloneElement(child, childProps);
153
- });
154
- }
155
116
  function renderAdditionalButtons() {
156
117
  if (!showAdditionalButtons) return null;
157
118
  return /*#__PURE__*/_react.default.createElement(_popover.default, {
158
119
  placement: "bottom-end",
159
- reference: splitButtonNode
160
- }, /*#__PURE__*/_react.default.createElement(_splitButtonChildren.default, {
161
- role: "menu",
120
+ reference: buttonNode
121
+ }, /*#__PURE__*/_react.default.createElement(_splitButtonChildren.default, _extends({}, wrapperProps, {
162
122
  "aria-label": text,
163
- "data-element": "additional-buttons",
164
- align: align,
165
- minWidth: minWidth,
166
- onKeyDown: handleKeyDown
167
- }, childrenWithProps()));
123
+ align: align
124
+ }), /*#__PURE__*/_react.default.createElement(_splitButton2.default.Provider, {
125
+ value: contextValue
126
+ }, children)));
168
127
  }
169
128
  const handleClick = (0, _useClickAwayListener.default)(hideButtons);
170
129
  const marginProps = (0, _utils.filterStyledSystemMarginProps)(rest);
171
130
  return /*#__PURE__*/_react.default.createElement(_splitButton.default, _extends({
172
131
  onMouseLeave: hideButtonsIfTriggerNotFocused,
173
132
  onClick: handleClick,
174
- ref: splitButtonNode
133
+ ref: buttonNode
175
134
  }, componentTags(), marginProps), renderMainButton(), renderAdditionalButtons());
176
135
  };
177
136
  exports.SplitButton = SplitButton;
@@ -0,0 +1 @@
1
+ export { default } from "./useChildButtons";
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "default", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _useChildButtons.default;
10
+ }
11
+ });
12
+ var _useChildButtons = _interopRequireDefault(require("./useChildButtons"));
13
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -0,0 +1,6 @@
1
+ {
2
+ "sideEffects": false,
3
+ "module": "../../../../esm/hooks/__internal__/useChildButtons/index.js",
4
+ "main": "./index.js",
5
+ "types": "./index.d.ts"
6
+ }
@@ -0,0 +1,21 @@
1
+ /// <reference types="react" />
2
+ declare const useChildButtons: (toggleButtonRef: React.RefObject<HTMLButtonElement>, widthRatio?: number) => {
3
+ showAdditionalButtons: boolean;
4
+ showButtons: () => void;
5
+ hideButtons: () => void;
6
+ buttonNode: import("react").RefObject<HTMLDivElement>;
7
+ hideButtonsIfTriggerNotFocused: () => void;
8
+ handleToggleButtonKeyDown: (ev: React.KeyboardEvent<HTMLButtonElement>) => void;
9
+ wrapperProps: {
10
+ role: string;
11
+ "data-element": string;
12
+ onKeyDown: (ev: any) => void;
13
+ minWidth: number;
14
+ ref: import("react").RefObject<HTMLDivElement>;
15
+ };
16
+ contextValue: {
17
+ inSplitButton: boolean;
18
+ onChildButtonClick: (childOnClick?: React.MouseEventHandler<HTMLButtonElement>) => (ev: React.MouseEvent<HTMLButtonElement>) => void;
19
+ };
20
+ };
21
+ export default useChildButtons;
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _react = require("react");
8
+ var _events = _interopRequireDefault(require("../../../__internal__/utils/helpers/events"));
9
+ var _useMenuKeyboardNavigation = _interopRequireDefault(require("../useMenuKeyboardNavigation"));
10
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
+ const useChildButtons = function (toggleButtonRef) {
12
+ let widthRatio = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
13
+ const [showAdditionalButtons, setShowAdditionalButtons] = (0, _react.useState)(false);
14
+ const [minWidth, setMinWidth] = (0, _react.useState)(0);
15
+ const buttonNode = (0, _react.useRef)(null);
16
+ const childrenContainer = (0, _react.useRef)(null);
17
+ const focusFirstChildButtonOnOpen = (0, _react.useRef)(false);
18
+ const hideButtons = (0, _react.useCallback)(() => {
19
+ setShowAdditionalButtons(false);
20
+ }, []);
21
+ function showButtons() {
22
+ setShowAdditionalButtons(true);
23
+
24
+ /* istanbul ignore else */
25
+ if (buttonNode.current) {
26
+ setMinWidth(widthRatio * buttonNode.current.getBoundingClientRect().width);
27
+ }
28
+ }
29
+ const getButtonChildren = (0, _react.useCallback)(() => childrenContainer.current?.querySelectorAll('[data-component="button"]'), []);
30
+ (0, _react.useEffect)(() => {
31
+ const firstChild = getButtonChildren()?.[0];
32
+ if (focusFirstChildButtonOnOpen.current && showAdditionalButtons && firstChild) {
33
+ focusFirstChildButtonOnOpen.current = false;
34
+ firstChild.focus();
35
+ }
36
+ }, [showAdditionalButtons, getButtonChildren]);
37
+ const handleToggleButtonKeyDown = ev => {
38
+ if (_events.default.isEnterKey(ev) || _events.default.isSpaceKey(ev) || _events.default.isDownKey(ev) || _events.default.isUpKey(ev)) {
39
+ ev.preventDefault();
40
+ if (!showAdditionalButtons) {
41
+ showButtons();
42
+ }
43
+ focusFirstChildButtonOnOpen.current = true;
44
+ }
45
+ };
46
+ const handleKeyDown = (0, _useMenuKeyboardNavigation.default)(toggleButtonRef, getButtonChildren, hideButtons, showAdditionalButtons);
47
+ const onChildButtonClick = childOnClick => ev => {
48
+ childOnClick?.(ev);
49
+ hideButtons();
50
+ toggleButtonRef.current?.focus();
51
+ };
52
+ const hideButtonsIfTriggerNotFocused = (0, _react.useCallback)(() => {
53
+ if (toggleButtonRef.current === document.activeElement) return;
54
+ setShowAdditionalButtons(false);
55
+ }, [toggleButtonRef]);
56
+ const wrapperProps = {
57
+ role: "menu",
58
+ "data-element": "additional-buttons",
59
+ onKeyDown: handleKeyDown,
60
+ minWidth,
61
+ ref: childrenContainer
62
+ };
63
+ const contextValue = {
64
+ inSplitButton: true,
65
+ onChildButtonClick
66
+ };
67
+ return {
68
+ showAdditionalButtons,
69
+ showButtons,
70
+ hideButtons,
71
+ buttonNode,
72
+ hideButtonsIfTriggerNotFocused,
73
+ handleToggleButtonKeyDown,
74
+ wrapperProps,
75
+ contextValue
76
+ };
77
+ };
78
+ var _default = useChildButtons;
79
+ exports.default = _default;
@@ -1,2 +1,2 @@
1
- declare const _default: (mainControlRef: React.RefObject<HTMLButtonElement>, childrenRefs: React.RefObject<HTMLButtonElement>[], hide: () => void, isOpen: boolean) => (ev: any) => void;
1
+ declare const _default: (mainControlRef: React.RefObject<HTMLButtonElement>, getButtonChildren: () => NodeListOf<HTMLButtonElement>, hide: () => void, isOpen: boolean) => (ev: any) => void;
2
2
  export default _default;
@@ -9,8 +9,7 @@ var _events = _interopRequireDefault(require("../../../__internal__/utils/helper
9
9
  var _focusTrapUtils = require("../../../__internal__/focus-trap/focus-trap-utils");
10
10
  var _useModalManager = _interopRequireDefault(require("../useModalManager"));
11
11
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12
- var _default = (mainControlRef, childrenRefs, hide, isOpen) => {
13
- const childrenLength = (0, _react.useMemo)(() => childrenRefs.length, [childrenRefs]);
12
+ var _default = (mainControlRef, getButtonChildren, hide, isOpen) => {
14
13
  const refocusMainControl = (0, _react.useCallback)(() => {
15
14
  hide();
16
15
  mainControlRef.current?.focus();
@@ -33,8 +32,10 @@ var _default = (mainControlRef, childrenRefs, hide, isOpen) => {
33
32
  if (!(_events.default.isEnterKey(ev) || _events.default.isSpaceKey(ev))) {
34
33
  ev.preventDefault();
35
34
  }
36
- const currentIndex = childrenRefs?.findIndex(node => node.current === document.activeElement);
35
+ const buttonChildren = getButtonChildren();
36
+ const childrenLength = buttonChildren?.length;
37
37
  let nextIndex = -1;
38
+ const currentIndex = Array.from(buttonChildren).indexOf(document.activeElement);
38
39
  const arrowModifierPressed = ev.ctrlKey || ev.metaKey;
39
40
  if (_events.default.isEndKey(ev) || arrowModifierPressed && _events.default.isDownKey(ev)) {
40
41
  nextIndex = childrenLength - 1;
@@ -61,16 +62,16 @@ var _default = (mainControlRef, childrenRefs, hide, isOpen) => {
61
62
  const elements = Array.from(document.querySelectorAll(_focusTrapUtils.defaultFocusableSelectors)).filter(el => Number(el.tabIndex) !== -1);
62
63
  const indexOf = elements.indexOf(mainControlRef.current);
63
64
  elements[indexOf + 1]?.focus();
64
- // // timeout enforces that the "hide" method will be run after browser focuses on the next element
65
+ // timeout enforces that the "hide" method will be run after browser focuses on the next element
65
66
  setTimeout(hide, 0);
66
67
  } else {
67
68
  nextIndex = currentIndex + 1;
68
69
  }
69
70
  }
70
71
  if (nextIndex > -1) {
71
- childrenRefs[nextIndex].current?.focus();
72
+ buttonChildren?.[nextIndex]?.focus();
72
73
  }
73
- }, [childrenLength, hide, refocusMainControl, childrenRefs, mainControlRef]);
74
+ }, [hide, refocusMainControl, mainControlRef, getButtonChildren]);
74
75
  return handleKeyDown;
75
76
  };
76
77
  exports.default = _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "carbon-react",
3
- "version": "119.9.1",
3
+ "version": "119.9.3",
4
4
  "description": "A library of reusable React components for easily building user interfaces.",
5
5
  "files": [
6
6
  "lib",