carbon-react 119.8.0 → 119.9.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.
Files changed (19) hide show
  1. package/esm/components/action-popover/action-popover-context.d.ts +2 -0
  2. package/esm/components/action-popover/action-popover-item/action-popover-item.component.d.ts +17 -2
  3. package/esm/components/action-popover/action-popover-item/action-popover-item.component.js +89 -41
  4. package/esm/components/action-popover/action-popover-menu/action-popover-menu.component.d.ts +6 -3
  5. package/esm/components/action-popover/action-popover-menu/action-popover-menu.component.js +19 -6
  6. package/esm/components/action-popover/action-popover.component.d.ts +5 -2
  7. package/esm/components/action-popover/action-popover.component.js +2 -0
  8. package/esm/components/action-popover/action-popover.style.d.ts +12 -4
  9. package/esm/components/action-popover/action-popover.style.js +107 -19
  10. package/lib/components/action-popover/action-popover-context.d.ts +2 -0
  11. package/lib/components/action-popover/action-popover-item/action-popover-item.component.d.ts +17 -2
  12. package/lib/components/action-popover/action-popover-item/action-popover-item.component.js +88 -40
  13. package/lib/components/action-popover/action-popover-menu/action-popover-menu.component.d.ts +6 -3
  14. package/lib/components/action-popover/action-popover-menu/action-popover-menu.component.js +18 -5
  15. package/lib/components/action-popover/action-popover.component.d.ts +5 -2
  16. package/lib/components/action-popover/action-popover.component.js +2 -0
  17. package/lib/components/action-popover/action-popover.style.d.ts +12 -4
  18. package/lib/components/action-popover/action-popover.style.js +109 -19
  19. package/package.json +1 -1
@@ -1,7 +1,9 @@
1
1
  import React from "react";
2
+ export declare type Alignment = "left" | "right";
2
3
  declare type ActionPopoverContextType = {
3
4
  setOpenPopover: (isOpen: boolean) => void;
4
5
  focusButton: () => void;
6
+ submenuPosition: Alignment;
5
7
  isOpenPopover: boolean;
6
8
  };
7
9
  declare const ActionPopoverContext: React.Context<ActionPopoverContextType | null>;
@@ -1,4 +1,5 @@
1
1
  import React from "react";
2
+ import { Alignment } from "../action-popover-context";
2
3
  import { IconType } from "../../icon";
3
4
  export interface ActionPopoverItemProps {
4
5
  /** The text label to display for this Item */
@@ -20,10 +21,24 @@ export interface ActionPopoverItemProps {
20
21
  /** @ignore @private */
21
22
  focusItem?: boolean;
22
23
  /** @ignore @private */
23
- horizontalAlignment?: "left" | "right";
24
+ horizontalAlignment?: Alignment;
25
+ /** @ignore @private */
26
+ childHasSubmenu?: boolean;
27
+ /** @ignore @private */
28
+ childHasIcon?: boolean;
29
+ /** @ignore @private */
30
+ currentSubmenuPosition?: Alignment;
31
+ /** @ignore @private */
32
+ setChildHasSubmenu?: (value: boolean) => void;
33
+ /** @ignore @private */
34
+ setChildHasIcon?: (value: boolean) => void;
35
+ /** @ignore @private */
36
+ setCurrentSubmenuPosition?: (value: Alignment) => void;
37
+ /** @ignore @private */
38
+ isASubmenu?: boolean;
24
39
  }
25
40
  export declare const ActionPopoverItem: {
26
- ({ children, icon, disabled, onClick: onClickProp, submenu, placement, focusItem, download, href, horizontalAlignment, ...rest }: ActionPopoverItemProps): React.JSX.Element;
41
+ ({ children, icon, disabled, onClick: onClickProp, submenu, placement, focusItem, download, href, horizontalAlignment, childHasSubmenu, childHasIcon, currentSubmenuPosition, setChildHasSubmenu, setChildHasIcon, setCurrentSubmenuPosition, isASubmenu, ...rest }: ActionPopoverItemProps): React.JSX.Element;
27
42
  displayName: string;
28
43
  };
29
44
  export default ActionPopoverItem;
@@ -2,7 +2,7 @@ function _extends() { _extends = Object.assign ? Object.assign.bind() : function
2
2
  import React, { useCallback, useEffect, useRef, useState, useContext } from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import invariant from "invariant";
5
- import { MenuItemIcon, SubMenuItemIcon, StyledMenuItem, StyledMenuItemWrapper } from "../action-popover.style";
5
+ import { MenuItemIcon, SubMenuItemIcon, StyledMenuItem, StyledMenuItemInnerText, StyledMenuItemOuterContainer, StyledMenuItemWrapper } from "../action-popover.style";
6
6
  import Events from "../../../__internal__/utils/helpers/events";
7
7
  import createGuid from "../../../__internal__/utils/helpers/guid";
8
8
  import ActionPopoverContext from "../action-popover-context";
@@ -12,33 +12,22 @@ const INTERVAL = 150;
12
12
  function checkRef(ref) {
13
13
  return Boolean(ref && ref.current);
14
14
  }
15
- function leftAlignSubmenu(ref, submenuRef) {
15
+ function calculateSubmenuPosition(ref, submenuRef, submenuPosition, currentSubmenuPosition) {
16
16
  /* istanbul ignore if */
17
- if (!ref.current || !submenuRef.current) return true;
17
+
18
+ if (!ref.current || !submenuRef.current) return currentSubmenuPosition || submenuPosition;
18
19
  const {
19
- left
20
+ left,
21
+ right
20
22
  } = ref.current.getBoundingClientRect();
21
23
  const {
22
24
  offsetWidth
23
25
  } = submenuRef.current;
24
- return left >= offsetWidth;
25
- }
26
- function getContainerPosition(itemRef, submenuRef, placement) {
27
- /* istanbul ignore if */
28
- if (!itemRef.current || !submenuRef.current) return undefined;
29
- const {
30
- offsetWidth: parentWidth
31
- } = itemRef.current;
32
- const {
33
- offsetWidth: submenuWidth
34
- } = submenuRef.current;
35
- const xPositionValue = leftAlignSubmenu(itemRef, submenuRef) ? -submenuWidth : parentWidth;
36
- const yPositionName = placement === "top" ? "bottom" : "top";
37
- return {
38
- left: xPositionValue,
39
- [yPositionName]: "calc(-1 * var(--spacing100))",
40
- right: "auto"
41
- };
26
+ const windowWidth = document.body.clientWidth;
27
+ if (submenuPosition === "left") {
28
+ return left >= offsetWidth ? "left" : "right";
29
+ }
30
+ return windowWidth >= right + offsetWidth ? "right" : "left";
42
31
  }
43
32
  export const ActionPopoverItem = _ref => {
44
33
  let {
@@ -52,6 +41,13 @@ export const ActionPopoverItem = _ref => {
52
41
  download,
53
42
  href,
54
43
  horizontalAlignment,
44
+ childHasSubmenu,
45
+ childHasIcon,
46
+ currentSubmenuPosition,
47
+ setChildHasSubmenu,
48
+ setChildHasIcon,
49
+ setCurrentSubmenuPosition,
50
+ isASubmenu = false,
55
51
  ...rest
56
52
  } = _ref;
57
53
  const l = useLocale();
@@ -61,14 +57,14 @@ export const ActionPopoverItem = _ref => {
61
57
  const {
62
58
  setOpenPopover,
63
59
  isOpenPopover,
64
- focusButton
60
+ focusButton,
61
+ submenuPosition
65
62
  } = context;
66
63
  const isHref = !!href;
67
64
  const [containerPosition, setContainerPosition] = useState(undefined);
68
65
  const [guid] = useState(createGuid());
69
66
  const [isOpen, setOpen] = useState(false);
70
67
  const [focusIndex, setFocusIndex] = useState(0);
71
- const [isLeftAligned, setIsLeftAligned] = useState(true);
72
68
  const submenuRef = useRef(null);
73
69
  const ref = useRef(null);
74
70
  const mouseEnterTimer = useRef(null);
@@ -78,19 +74,48 @@ export const ActionPopoverItem = _ref => {
78
74
  setOpen(false);
79
75
  }
80
76
  }, [isOpenPopover]);
77
+ useEffect(() => {
78
+ if (icon) {
79
+ setChildHasIcon?.(true);
80
+ }
81
+ if (submenu) {
82
+ setChildHasSubmenu?.(true);
83
+ }
84
+ }, [icon, setChildHasSubmenu, setChildHasIcon, submenu]);
81
85
  const alignSubmenu = useCallback(() => {
82
- if (checkRef(ref) && checkRef(submenuRef) && submenu) {
83
- const align = leftAlignSubmenu(ref, submenuRef);
84
- setIsLeftAligned(align);
85
- setContainerPosition(getContainerPosition(ref, submenuRef, placement));
86
+ const checkCalculatedSubmenuPosition = calculateSubmenuPosition(ref, submenuRef, submenuPosition, currentSubmenuPosition);
87
+ setCurrentSubmenuPosition?.(checkCalculatedSubmenuPosition);
88
+ return checkRef(ref) && checkRef(submenuRef) && submenu;
89
+ }, [submenu, setCurrentSubmenuPosition, submenuPosition, currentSubmenuPosition]);
90
+ useEffect(() => {
91
+ const getContainerPosition = () => {
92
+ /* istanbul ignore if */
93
+ if (!ref.current || !submenuRef.current) return undefined;
94
+ const {
95
+ offsetWidth: submenuWidth
96
+ } = submenuRef.current;
97
+ const leftAlignedSubmenu = currentSubmenuPosition === "left";
98
+ const leftValue = leftAlignedSubmenu ? -submenuWidth : "auto";
99
+ const rightValue = leftAlignedSubmenu ? "auto" : -submenuWidth;
100
+ const yPositionName = placement === "top" ? "bottom" : "top";
101
+ return {
102
+ left: leftValue,
103
+ [yPositionName]: "calc(-1 * var(--spacing100))",
104
+ right: rightValue
105
+ };
106
+ };
107
+ setContainerPosition(getContainerPosition);
108
+ }, [submenu, currentSubmenuPosition, placement]);
109
+ useEffect(() => {
110
+ if (submenu) {
111
+ alignSubmenu();
86
112
  }
87
- }, [submenu, placement]);
113
+ }, [alignSubmenu, submenu]);
88
114
  useEffect(() => {
89
- alignSubmenu();
90
115
  if (focusItem) {
91
116
  ref.current?.focus();
92
117
  }
93
- }, [alignSubmenu, focusItem]);
118
+ }, [focusItem]);
94
119
  useEffect(() => {
95
120
  return function cleanup() {
96
121
  if (mouseEnterTimer.current) clearTimeout(mouseEnterTimer.current);
@@ -123,7 +148,7 @@ export const ActionPopoverItem = _ref => {
123
148
  e.stopPropagation();
124
149
  } else if (!disabled) {
125
150
  if (submenu) {
126
- if (isLeftAligned) {
151
+ if (currentSubmenuPosition === "left") {
127
152
  // LEFT: open if has submenu and left aligned otherwise close submenu
128
153
  if (Events.isLeftKey(e) || Events.isEnterKey(e)) {
129
154
  setOpen(true);
@@ -159,7 +184,7 @@ export const ActionPopoverItem = _ref => {
159
184
  } else if (Events.isEnterKey(e)) {
160
185
  e.stopPropagation();
161
186
  }
162
- }, [disabled, download, isHref, isLeftAligned, onClick, submenu]);
187
+ }, [disabled, download, isHref, onClick, submenu, currentSubmenuPosition]);
163
188
  const itemSubmenuProps = {
164
189
  ...(!disabled && {
165
190
  onClick: e => {
@@ -195,8 +220,15 @@ export const ActionPopoverItem = _ref => {
195
220
  };
196
221
  const renderMenuItemIcon = () => {
197
222
  return icon && /*#__PURE__*/React.createElement(MenuItemIcon, {
198
- as: undefined,
199
- type: icon
223
+ type: icon,
224
+ "data-element": "action-popover-menu-item-icon",
225
+ horizontalAlignment: horizontalAlignment,
226
+ submenuPosition: currentSubmenuPosition,
227
+ childHasIcon: childHasIcon,
228
+ childHasSubmenu: childHasSubmenu,
229
+ hasIcon: !!icon,
230
+ hasSubmenu: !!submenu,
231
+ isASubmenu: isASubmenu
200
232
  });
201
233
  };
202
234
  return /*#__PURE__*/React.createElement(StyledMenuItemWrapper, submenu && wrapperDivProps, /*#__PURE__*/React.createElement("div", {
@@ -209,17 +241,31 @@ export const ActionPopoverItem = _ref => {
209
241
  role: "menuitem",
210
242
  tabIndex: 0,
211
243
  isDisabled: disabled,
212
- horizontalAlignment: horizontalAlignment
244
+ horizontalAlignment: horizontalAlignment,
245
+ submenuPosition: currentSubmenuPosition,
246
+ hasSubmenu: !!submenu,
247
+ childHasSubmenu: childHasSubmenu
213
248
  }, disabled && {
214
249
  "aria-disabled": true
215
250
  }, isHref && {
216
251
  as: "a",
217
252
  download,
218
253
  href
219
- }, submenu && itemSubmenuProps), submenu && checkRef(ref) && isLeftAligned ? /*#__PURE__*/React.createElement(SubMenuItemIcon, {
220
- type: "chevron_left"
221
- }) : null, horizontalAlignment === "left" ? renderMenuItemIcon() : null, children, horizontalAlignment === "right" ? renderMenuItemIcon() : null, submenu && checkRef(ref) && !isLeftAligned ? /*#__PURE__*/React.createElement(SubMenuItemIcon, {
222
- type: "chevron_right"
254
+ }, submenu && itemSubmenuProps), submenu && checkRef(ref) && currentSubmenuPosition === "left" ? /*#__PURE__*/React.createElement(SubMenuItemIcon, {
255
+ "data-element": "action-popover-menu-item-chevron",
256
+ type: "chevron_left_thick"
257
+ }) : null, /*#__PURE__*/React.createElement(StyledMenuItemOuterContainer, null, horizontalAlignment === "left" ? renderMenuItemIcon() : null, /*#__PURE__*/React.createElement(StyledMenuItemInnerText, {
258
+ "data-element": "action-popover-menu-item-inner-text",
259
+ horizontalAlignment: horizontalAlignment,
260
+ submenuPosition: currentSubmenuPosition,
261
+ isASubmenu: isASubmenu,
262
+ childHasSubmenu: childHasSubmenu,
263
+ childHasIcon: childHasIcon,
264
+ hasIcon: !!icon,
265
+ hasSubmenu: !!submenu
266
+ }, children), horizontalAlignment === "right" ? renderMenuItemIcon() : null), submenu && checkRef(ref) && currentSubmenuPosition === "right" ? /*#__PURE__*/React.createElement(SubMenuItemIcon, {
267
+ "data-element": "action-popover-menu-item-chevron",
268
+ type: "chevron_right_thick"
223
269
  }) : null), /*#__PURE__*/React.isValidElement(submenu) ? /*#__PURE__*/React.cloneElement(submenu, {
224
270
  parentID: `ActionPopoverItem_${guid}`,
225
271
  menuID: `ActionPopoverMenu_${guid}`,
@@ -229,7 +275,9 @@ export const ActionPopoverItem = _ref => {
229
275
  style: containerPosition,
230
276
  setOpen,
231
277
  setFocusIndex,
232
- focusIndex
278
+ focusIndex,
279
+ isASubmenu: true,
280
+ horizontalAlignment
233
281
  }) : null));
234
282
  };
235
283
  ActionPopoverItem.displayName = "ActionPopoverItem";
@@ -1,4 +1,5 @@
1
1
  import React from "react";
2
+ import { Alignment } from "../action-popover-context";
2
3
  export interface ActionPopoverMenuBaseProps {
3
4
  /** Children for the menu */
4
5
  children?: React.ReactNode;
@@ -15,19 +16,21 @@ export interface ActionPopoverMenuBaseProps {
15
16
  /** Unique ID for the menu's parent */
16
17
  parentID?: string;
17
18
  /** Horizontal alignment of menu items content */
18
- horizontalAlignment?: "left" | "right";
19
+ horizontalAlignment?: Alignment;
19
20
  /** Set whether the menu should open above or below the button */
20
21
  placement?: "bottom" | "top";
21
22
  /** @ignore @private */
22
23
  role?: string;
23
24
  /** @ignore @private */
25
+ isASubmenu?: boolean;
26
+ /** @ignore @private */
24
27
  "data-element"?: string;
25
28
  /** @ignore @private */
26
29
  style?: {
27
- left: number;
30
+ left: string | number;
28
31
  top?: string;
29
32
  bottom?: string;
30
- right: "auto";
33
+ right: string | number;
31
34
  };
32
35
  }
33
36
  export interface ActionPopoverMenuProps extends ActionPopoverMenuBaseProps, React.RefAttributes<HTMLDivElement> {
@@ -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, { useCallback, useMemo, useContext } from "react";
2
+ import React, { useCallback, useMemo, useContext, useState } from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import invariant from "invariant";
5
5
  import { Menu } from "../action-popover.style";
@@ -18,12 +18,14 @@ const ActionPopoverMenu = /*#__PURE__*/React.forwardRef((_ref, ref) => {
18
18
  setFocusIndex,
19
19
  placement = "bottom",
20
20
  horizontalAlignment,
21
+ isASubmenu,
21
22
  ...rest
22
23
  } = _ref;
23
24
  const context = useContext(ActionPopoverContext);
24
25
  !context ? process.env.NODE_ENV !== "production" ? invariant(false, "ActionPopoverMenu must be used within an ActionPopover component") : invariant(false) : void 0;
25
26
  const {
26
- focusButton
27
+ focusButton,
28
+ submenuPosition
27
29
  } = context;
28
30
  !(setOpen && setFocusIndex && typeof focusIndex !== "undefined") ? process.env.NODE_ENV !== "production" ? invariant(false, "ActionPopoverMenu must be used within an ActionPopover or ActionPopoverItem component") : invariant(false) : void 0;
29
31
  const hasProperChildren = useMemo(() => {
@@ -92,6 +94,9 @@ const ActionPopoverMenu = /*#__PURE__*/React.forwardRef((_ref, ref) => {
92
94
  }
93
95
  }
94
96
  }, [focusButton, setOpen, focusIndex, items, setFocusIndex]);
97
+ const [childHasSubmenu, setChildHasSubmenu] = useState(false);
98
+ const [childHasIcon, setChildHasIcon] = useState(false);
99
+ const [currentSubmenuPosition, setCurrentSubmenuPosition] = useState(submenuPosition);
95
100
  const clonedChildren = useMemo(() => {
96
101
  let index = 0;
97
102
  return React.Children.map(children, child => {
@@ -100,12 +105,19 @@ const ActionPopoverMenu = /*#__PURE__*/React.forwardRef((_ref, ref) => {
100
105
  return /*#__PURE__*/React.cloneElement(child, {
101
106
  focusItem: isOpen && focusIndex === index - 1,
102
107
  placement: child.props.submenu ? placement : undefined,
103
- horizontalAlignment
108
+ horizontalAlignment,
109
+ childHasSubmenu,
110
+ setChildHasSubmenu,
111
+ childHasIcon,
112
+ setChildHasIcon,
113
+ currentSubmenuPosition,
114
+ setCurrentSubmenuPosition,
115
+ isASubmenu
104
116
  });
105
117
  }
106
118
  return child;
107
119
  });
108
- }, [children, focusIndex, isOpen, placement, horizontalAlignment]);
120
+ }, [children, focusIndex, isOpen, placement, horizontalAlignment, childHasSubmenu, childHasIcon, currentSubmenuPosition, isASubmenu]);
109
121
  return /*#__PURE__*/React.createElement(Menu, _extends({
110
122
  "data-component": "action-popover",
111
123
  isOpen: isOpen,
@@ -121,6 +133,7 @@ ActionPopoverMenu.propTypes = {
121
133
  "data-element": PropTypes.string,
122
134
  "focusIndex": PropTypes.number,
123
135
  "horizontalAlignment": PropTypes.oneOf(["left", "right"]),
136
+ "isASubmenu": PropTypes.bool,
124
137
  "isOpen": PropTypes.bool,
125
138
  "menuID": PropTypes.string,
126
139
  "parentID": PropTypes.string,
@@ -130,8 +143,8 @@ ActionPopoverMenu.propTypes = {
130
143
  "setOpen": PropTypes.func,
131
144
  "style": PropTypes.shape({
132
145
  "bottom": PropTypes.string,
133
- "left": PropTypes.number.isRequired,
134
- "right": PropTypes.oneOf(["auto"]).isRequired,
146
+ "left": PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
147
+ "right": PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
135
148
  "top": PropTypes.string
136
149
  })
137
150
  };
@@ -1,5 +1,6 @@
1
1
  import React from "react";
2
2
  import { MarginProps } from "styled-system";
3
+ import { Alignment } from "./action-popover-context";
3
4
  export interface RenderButtonProps {
4
5
  tabIndex: number;
5
6
  "data-element": string;
@@ -14,7 +15,9 @@ export interface ActionPopoverProps extends MarginProps {
14
15
  /** Children for popover component */
15
16
  children?: React.ReactNode;
16
17
  /** Horizontal alignment of menu items content */
17
- horizontalAlignment?: "left" | "right";
18
+ horizontalAlignment?: Alignment;
19
+ /** Sets submenu position */
20
+ submenuPosition?: Alignment;
18
21
  /** Unique ID */
19
22
  id?: string;
20
23
  /** Callback to be called on menu open */
@@ -28,5 +31,5 @@ export interface ActionPopoverProps extends MarginProps {
28
31
  /** Boolean to control whether menu should align to right */
29
32
  rightAlignMenu?: boolean;
30
33
  }
31
- export declare const ActionPopover: ({ children, id, onOpen, onClose, rightAlignMenu, renderButton, placement, horizontalAlignment, ...rest }: ActionPopoverProps) => React.JSX.Element;
34
+ export declare const ActionPopover: ({ children, id, onOpen, onClose, rightAlignMenu, renderButton, placement, horizontalAlignment, submenuPosition, ...rest }: ActionPopoverProps) => React.JSX.Element;
32
35
  export default ActionPopover;
@@ -24,6 +24,7 @@ export const ActionPopover = _ref => {
24
24
  renderButton,
25
25
  placement = "bottom",
26
26
  horizontalAlignment = "left",
27
+ submenuPosition = "left",
27
28
  ...rest
28
29
  } = _ref;
29
30
  const l = useLocale();
@@ -185,6 +186,7 @@ export const ActionPopover = _ref => {
185
186
  value: {
186
187
  setOpenPopover: setOpen,
187
188
  focusButton,
189
+ submenuPosition,
188
190
  isOpenPopover: isOpen
189
191
  }
190
192
  }, isOpen && /*#__PURE__*/React.createElement(Popover, {
@@ -4,9 +4,17 @@ declare const Menu: import("styled-components").StyledComponent<"div", any, {
4
4
  }, never>;
5
5
  declare type StyledMenuItemProps = {
6
6
  isDisabled: boolean;
7
- horizontalAlignment: "left" | "right";
7
+ horizontalAlignment?: "left" | "right";
8
+ submenuPosition?: "left" | "right";
9
+ childHasSubmenu?: boolean;
10
+ childHasIcon?: boolean;
11
+ hasSubmenu?: boolean;
12
+ hasIcon?: boolean;
13
+ isASubmenu?: boolean;
8
14
  };
9
- declare const StyledMenuItem: import("styled-components").StyledComponent<"button", any, StyledMenuItemProps, never>;
15
+ declare const StyledMenuItemInnerText: import("styled-components").StyledComponent<"div", any, Omit<StyledMenuItemProps, "isDisabled">, never>;
16
+ declare const StyledMenuItemOuterContainer: import("styled-components").StyledComponent<"div", any, {}, never>;
17
+ declare const StyledMenuItem: import("styled-components").StyledComponent<"button", any, Omit<StyledMenuItemProps, "variant">, never>;
10
18
  declare const StyledMenuItemWrapper: import("styled-components").StyledComponent<"div", any, {}, never>;
11
19
  declare const MenuItemDivider: import("styled-components").StyledComponent<"div", any, {
12
20
  "data-element": string;
@@ -14,7 +22,7 @@ declare const MenuItemDivider: import("styled-components").StyledComponent<"div"
14
22
  declare const MenuButton: import("styled-components").StyledComponent<"div", any, {}, never>;
15
23
  declare const ButtonIcon: import("styled-components").StyledComponent<import("react").ForwardRefExoticComponent<import("../icon").IconProps & import("react").RefAttributes<HTMLSpanElement>>, any, {}, never>;
16
24
  declare const StyledButtonIcon: import("styled-components").StyledComponent<"div", any, {}, never>;
17
- declare const MenuItemIcon: import("styled-components").StyledComponent<import("react").ForwardRefExoticComponent<import("../icon").IconProps & import("react").RefAttributes<HTMLSpanElement>>, any, {}, never>;
25
+ declare const MenuItemIcon: import("styled-components").StyledComponent<import("react").ForwardRefExoticComponent<import("../icon").IconProps & import("react").RefAttributes<HTMLSpanElement>>, any, Omit<StyledMenuItemProps, "isDisabled">, never>;
18
26
  declare const SubMenuItemIcon: import("styled-components").StyledComponent<import("react").ForwardRefExoticComponent<import("../icon").IconProps & import("react").RefAttributes<HTMLSpanElement>>, any, {}, never>;
19
27
  declare const MenuButtonOverrideWrapper: import("styled-components").StyledComponent<"div", any, {}, never>;
20
- export { Menu, MenuButton, ButtonIcon, StyledButtonIcon, MenuItemIcon, MenuItemDivider, SubMenuItemIcon, MenuButtonOverrideWrapper, StyledMenuItem, StyledMenuItemWrapper, };
28
+ export { Menu, MenuButton, ButtonIcon, StyledButtonIcon, MenuItemIcon, MenuItemDivider, SubMenuItemIcon, MenuButtonOverrideWrapper, StyledMenuItemInnerText, StyledMenuItemOuterContainer, StyledMenuItem, StyledMenuItemWrapper, };
@@ -24,7 +24,85 @@ const Menu = styled.div`
24
24
  return `${theme.zIndex?.popover}`;
25
25
  }}; // TODO (tokens): implement elevation tokens - FE-4437
26
26
  `;
27
+ function getPaddingValues(childHasSubmenu, childHasIcon, hasIcon, hasSubmenu) {
28
+ if (!childHasIcon && childHasSubmenu && !hasIcon && !hasSubmenu) {
29
+ return "var(--spacing400)";
30
+ }
31
+ if (childHasIcon && childHasSubmenu && !hasIcon && hasSubmenu) {
32
+ return "var(--spacing600)";
33
+ }
34
+ if (childHasIcon && childHasSubmenu && !hasIcon && !hasSubmenu) {
35
+ return "var(--spacing900)";
36
+ }
37
+ return "var(--spacing100)";
38
+ }
39
+ function getIconPaddingValues(index, horizontalAlignment, submenuPosition, siblingsHaveIconAndSubmenu, isASubmenu) {
40
+ const sameAlignment = horizontalAlignment === "left" && submenuPosition === "left" || horizontalAlignment === "right" && submenuPosition === "right";
41
+ if (siblingsHaveIconAndSubmenu && sameAlignment) {
42
+ if (horizontalAlignment === "left") {
43
+ return index === 1 ? "var(--spacing100)" : "var(--spacing400)";
44
+ }
45
+ return index === 1 ? "var(--spacing400)" : "var(--spacing100)";
46
+ }
47
+ if (isASubmenu) {
48
+ if (horizontalAlignment === "left") {
49
+ return index === 1 ? "var(--spacing100)" : "var(--spacing000)";
50
+ }
51
+ return index === 1 ? "var(--spacing000)" : "var(--spacing100)";
52
+ }
53
+ return "var(--spacing100)";
54
+ }
55
+ const StyledMenuItemInnerText = styled.div`
56
+ ${_ref3 => {
57
+ let {
58
+ childHasSubmenu,
59
+ childHasIcon,
60
+ hasIcon,
61
+ hasSubmenu,
62
+ submenuPosition,
63
+ horizontalAlignment,
64
+ isASubmenu
65
+ } = _ref3;
66
+ return css`
67
+ padding-left: ${isASubmenu ? `var(--spacing000)` : `var(--spacing100)`};
68
+ padding-right: ${isASubmenu ? `var(--spacing000)` : `var(--spacing100)`};
69
+
70
+ ${horizontalAlignment === "left" && submenuPosition === "left" && !isASubmenu && css`
71
+ padding-left: ${getPaddingValues(childHasSubmenu, childHasIcon, hasIcon, hasSubmenu)};
72
+ `}
73
+
74
+ ${horizontalAlignment === "right" && submenuPosition === "right" && !isASubmenu && css`
75
+ padding-right: ${getPaddingValues(childHasSubmenu, childHasIcon, hasIcon, hasSubmenu)};
76
+ `}
77
+ `;
78
+ }}
79
+ `;
80
+ const StyledMenuItemOuterContainer = styled.div`
81
+ display: inherit;
82
+ `;
27
83
  const StyledMenuItem = styled.button`
84
+ ${_ref4 => {
85
+ let {
86
+ horizontalAlignment,
87
+ submenuPosition,
88
+ childHasSubmenu,
89
+ hasSubmenu
90
+ } = _ref4;
91
+ return css`
92
+ justify-content: ${horizontalAlignment === "left" ? "flex-start" : "flex-end"};
93
+
94
+ ${horizontalAlignment === "left" && submenuPosition === "right" && css`
95
+ justify-content: space-between;
96
+ `}
97
+
98
+ ${horizontalAlignment === "right" && submenuPosition === "left" && css`
99
+ ${childHasSubmenu && hasSubmenu && css`
100
+ justify-content: space-between;
101
+ `}
102
+ `}
103
+ `;
104
+ }}
105
+
28
106
  text-decoration: none;
29
107
  background-color: var(--colorsActionMajorYang100);
30
108
  cursor: pointer;
@@ -41,12 +119,6 @@ const StyledMenuItem = styled.button`
41
119
  color: var(--colorsUtilityYin090);
42
120
  font-size: 14px;
43
121
  font-weight: 700;
44
- justify-content: ${_ref3 => {
45
- let {
46
- horizontalAlignment
47
- } = _ref3;
48
- return horizontalAlignment === "left" ? "flex-start" : "flex-end";
49
- }};
50
122
 
51
123
  &:focus {
52
124
  outline: var(--borderWidth300) solid var(--colorsSemanticFocus500);
@@ -54,10 +126,10 @@ const StyledMenuItem = styled.button`
54
126
  border-radius: var(--borderRadius000);
55
127
  }
56
128
 
57
- ${_ref4 => {
129
+ ${_ref5 => {
58
130
  let {
59
131
  isDisabled
60
- } = _ref4;
132
+ } = _ref5;
61
133
  return isDisabled && css`
62
134
  color: var(--colorsUtilityYin030);
63
135
  cursor: not-allowed;
@@ -69,10 +141,10 @@ const StyledMenuItem = styled.button`
69
141
  `;
70
142
  }}
71
143
 
72
- ${_ref5 => {
144
+ ${_ref6 => {
73
145
  let {
74
146
  isDisabled
75
- } = _ref5;
147
+ } = _ref6;
76
148
  return !isDisabled && css`
77
149
  &:focus,
78
150
  &:hover {
@@ -117,21 +189,37 @@ const StyledButtonIcon = styled.div`
117
189
  }
118
190
  `;
119
191
  const MenuItemIcon = styled(Icon)`
120
- padding: var(--spacing100);
121
- color: var(--colorsUtilityYin065);
192
+ ${_ref7 => {
193
+ let {
194
+ horizontalAlignment,
195
+ submenuPosition,
196
+ childHasIcon,
197
+ childHasSubmenu,
198
+ hasIcon,
199
+ hasSubmenu,
200
+ isASubmenu
201
+ } = _ref7;
202
+ return css`
203
+ justify-content: ${horizontalAlignment};
204
+ padding: var(--spacing100)
205
+ ${getIconPaddingValues(1, horizontalAlignment, submenuPosition, childHasIcon && childHasSubmenu && hasIcon && !hasSubmenu, isASubmenu)}
206
+ var(--spacing100)
207
+ ${getIconPaddingValues(2, horizontalAlignment, submenuPosition, childHasIcon && childHasSubmenu && hasIcon && !hasSubmenu, isASubmenu)};
208
+ color: var(--colorsUtilityYin065);
209
+ `;
210
+ }}
122
211
  `;
123
212
  const SubMenuItemIcon = styled(ButtonIcon)`
124
- ${_ref6 => {
213
+ ${_ref8 => {
125
214
  let {
126
215
  type
127
- } = _ref6;
216
+ } = _ref8;
128
217
  return css`
129
- position: absolute;
130
- ${type === "chevron_left" && css`
131
- left: -2px;
218
+ ${type === "chevron_left_thick" && css`
219
+ left: -5px;
132
220
  `}
133
221
 
134
- ${type === "chevron_right" && css`
222
+ ${type === "chevron_right_thick" && css`
135
223
  right: -5px;
136
224
  ${isSafari(navigator) && css`
137
225
  top: var(--sizing100);
@@ -156,4 +244,4 @@ const MenuButtonOverrideWrapper = styled.div`
156
244
  }
157
245
  }
158
246
  `;
159
- export { Menu, MenuButton, ButtonIcon, StyledButtonIcon, MenuItemIcon, MenuItemDivider, SubMenuItemIcon, MenuButtonOverrideWrapper, StyledMenuItem, StyledMenuItemWrapper };
247
+ export { Menu, MenuButton, ButtonIcon, StyledButtonIcon, MenuItemIcon, MenuItemDivider, SubMenuItemIcon, MenuButtonOverrideWrapper, StyledMenuItemInnerText, StyledMenuItemOuterContainer, StyledMenuItem, StyledMenuItemWrapper };
@@ -1,7 +1,9 @@
1
1
  import React from "react";
2
+ export declare type Alignment = "left" | "right";
2
3
  declare type ActionPopoverContextType = {
3
4
  setOpenPopover: (isOpen: boolean) => void;
4
5
  focusButton: () => void;
6
+ submenuPosition: Alignment;
5
7
  isOpenPopover: boolean;
6
8
  };
7
9
  declare const ActionPopoverContext: React.Context<ActionPopoverContextType | null>;
@@ -1,4 +1,5 @@
1
1
  import React from "react";
2
+ import { Alignment } from "../action-popover-context";
2
3
  import { IconType } from "../../icon";
3
4
  export interface ActionPopoverItemProps {
4
5
  /** The text label to display for this Item */
@@ -20,10 +21,24 @@ export interface ActionPopoverItemProps {
20
21
  /** @ignore @private */
21
22
  focusItem?: boolean;
22
23
  /** @ignore @private */
23
- horizontalAlignment?: "left" | "right";
24
+ horizontalAlignment?: Alignment;
25
+ /** @ignore @private */
26
+ childHasSubmenu?: boolean;
27
+ /** @ignore @private */
28
+ childHasIcon?: boolean;
29
+ /** @ignore @private */
30
+ currentSubmenuPosition?: Alignment;
31
+ /** @ignore @private */
32
+ setChildHasSubmenu?: (value: boolean) => void;
33
+ /** @ignore @private */
34
+ setChildHasIcon?: (value: boolean) => void;
35
+ /** @ignore @private */
36
+ setCurrentSubmenuPosition?: (value: Alignment) => void;
37
+ /** @ignore @private */
38
+ isASubmenu?: boolean;
24
39
  }
25
40
  export declare const ActionPopoverItem: {
26
- ({ children, icon, disabled, onClick: onClickProp, submenu, placement, focusItem, download, href, horizontalAlignment, ...rest }: ActionPopoverItemProps): React.JSX.Element;
41
+ ({ children, icon, disabled, onClick: onClickProp, submenu, placement, focusItem, download, href, horizontalAlignment, childHasSubmenu, childHasIcon, currentSubmenuPosition, setChildHasSubmenu, setChildHasIcon, setCurrentSubmenuPosition, isASubmenu, ...rest }: ActionPopoverItemProps): React.JSX.Element;
27
42
  displayName: string;
28
43
  };
29
44
  export default ActionPopoverItem;
@@ -21,33 +21,22 @@ const INTERVAL = 150;
21
21
  function checkRef(ref) {
22
22
  return Boolean(ref && ref.current);
23
23
  }
24
- function leftAlignSubmenu(ref, submenuRef) {
24
+ function calculateSubmenuPosition(ref, submenuRef, submenuPosition, currentSubmenuPosition) {
25
25
  /* istanbul ignore if */
26
- if (!ref.current || !submenuRef.current) return true;
26
+
27
+ if (!ref.current || !submenuRef.current) return currentSubmenuPosition || submenuPosition;
27
28
  const {
28
- left
29
+ left,
30
+ right
29
31
  } = ref.current.getBoundingClientRect();
30
32
  const {
31
33
  offsetWidth
32
34
  } = submenuRef.current;
33
- return left >= offsetWidth;
34
- }
35
- function getContainerPosition(itemRef, submenuRef, placement) {
36
- /* istanbul ignore if */
37
- if (!itemRef.current || !submenuRef.current) return undefined;
38
- const {
39
- offsetWidth: parentWidth
40
- } = itemRef.current;
41
- const {
42
- offsetWidth: submenuWidth
43
- } = submenuRef.current;
44
- const xPositionValue = leftAlignSubmenu(itemRef, submenuRef) ? -submenuWidth : parentWidth;
45
- const yPositionName = placement === "top" ? "bottom" : "top";
46
- return {
47
- left: xPositionValue,
48
- [yPositionName]: "calc(-1 * var(--spacing100))",
49
- right: "auto"
50
- };
35
+ const windowWidth = document.body.clientWidth;
36
+ if (submenuPosition === "left") {
37
+ return left >= offsetWidth ? "left" : "right";
38
+ }
39
+ return windowWidth >= right + offsetWidth ? "right" : "left";
51
40
  }
52
41
  const ActionPopoverItem = _ref => {
53
42
  let {
@@ -61,6 +50,13 @@ const ActionPopoverItem = _ref => {
61
50
  download,
62
51
  href,
63
52
  horizontalAlignment,
53
+ childHasSubmenu,
54
+ childHasIcon,
55
+ currentSubmenuPosition,
56
+ setChildHasSubmenu,
57
+ setChildHasIcon,
58
+ setCurrentSubmenuPosition,
59
+ isASubmenu = false,
64
60
  ...rest
65
61
  } = _ref;
66
62
  const l = (0, _useLocale.default)();
@@ -70,14 +66,14 @@ const ActionPopoverItem = _ref => {
70
66
  const {
71
67
  setOpenPopover,
72
68
  isOpenPopover,
73
- focusButton
69
+ focusButton,
70
+ submenuPosition
74
71
  } = context;
75
72
  const isHref = !!href;
76
73
  const [containerPosition, setContainerPosition] = (0, _react.useState)(undefined);
77
74
  const [guid] = (0, _react.useState)((0, _guid.default)());
78
75
  const [isOpen, setOpen] = (0, _react.useState)(false);
79
76
  const [focusIndex, setFocusIndex] = (0, _react.useState)(0);
80
- const [isLeftAligned, setIsLeftAligned] = (0, _react.useState)(true);
81
77
  const submenuRef = (0, _react.useRef)(null);
82
78
  const ref = (0, _react.useRef)(null);
83
79
  const mouseEnterTimer = (0, _react.useRef)(null);
@@ -87,19 +83,48 @@ const ActionPopoverItem = _ref => {
87
83
  setOpen(false);
88
84
  }
89
85
  }, [isOpenPopover]);
86
+ (0, _react.useEffect)(() => {
87
+ if (icon) {
88
+ setChildHasIcon?.(true);
89
+ }
90
+ if (submenu) {
91
+ setChildHasSubmenu?.(true);
92
+ }
93
+ }, [icon, setChildHasSubmenu, setChildHasIcon, submenu]);
90
94
  const alignSubmenu = (0, _react.useCallback)(() => {
91
- if (checkRef(ref) && checkRef(submenuRef) && submenu) {
92
- const align = leftAlignSubmenu(ref, submenuRef);
93
- setIsLeftAligned(align);
94
- setContainerPosition(getContainerPosition(ref, submenuRef, placement));
95
+ const checkCalculatedSubmenuPosition = calculateSubmenuPosition(ref, submenuRef, submenuPosition, currentSubmenuPosition);
96
+ setCurrentSubmenuPosition?.(checkCalculatedSubmenuPosition);
97
+ return checkRef(ref) && checkRef(submenuRef) && submenu;
98
+ }, [submenu, setCurrentSubmenuPosition, submenuPosition, currentSubmenuPosition]);
99
+ (0, _react.useEffect)(() => {
100
+ const getContainerPosition = () => {
101
+ /* istanbul ignore if */
102
+ if (!ref.current || !submenuRef.current) return undefined;
103
+ const {
104
+ offsetWidth: submenuWidth
105
+ } = submenuRef.current;
106
+ const leftAlignedSubmenu = currentSubmenuPosition === "left";
107
+ const leftValue = leftAlignedSubmenu ? -submenuWidth : "auto";
108
+ const rightValue = leftAlignedSubmenu ? "auto" : -submenuWidth;
109
+ const yPositionName = placement === "top" ? "bottom" : "top";
110
+ return {
111
+ left: leftValue,
112
+ [yPositionName]: "calc(-1 * var(--spacing100))",
113
+ right: rightValue
114
+ };
115
+ };
116
+ setContainerPosition(getContainerPosition);
117
+ }, [submenu, currentSubmenuPosition, placement]);
118
+ (0, _react.useEffect)(() => {
119
+ if (submenu) {
120
+ alignSubmenu();
95
121
  }
96
- }, [submenu, placement]);
122
+ }, [alignSubmenu, submenu]);
97
123
  (0, _react.useEffect)(() => {
98
- alignSubmenu();
99
124
  if (focusItem) {
100
125
  ref.current?.focus();
101
126
  }
102
- }, [alignSubmenu, focusItem]);
127
+ }, [focusItem]);
103
128
  (0, _react.useEffect)(() => {
104
129
  return function cleanup() {
105
130
  if (mouseEnterTimer.current) clearTimeout(mouseEnterTimer.current);
@@ -132,7 +157,7 @@ const ActionPopoverItem = _ref => {
132
157
  e.stopPropagation();
133
158
  } else if (!disabled) {
134
159
  if (submenu) {
135
- if (isLeftAligned) {
160
+ if (currentSubmenuPosition === "left") {
136
161
  // LEFT: open if has submenu and left aligned otherwise close submenu
137
162
  if (_events.default.isLeftKey(e) || _events.default.isEnterKey(e)) {
138
163
  setOpen(true);
@@ -168,7 +193,7 @@ const ActionPopoverItem = _ref => {
168
193
  } else if (_events.default.isEnterKey(e)) {
169
194
  e.stopPropagation();
170
195
  }
171
- }, [disabled, download, isHref, isLeftAligned, onClick, submenu]);
196
+ }, [disabled, download, isHref, onClick, submenu, currentSubmenuPosition]);
172
197
  const itemSubmenuProps = {
173
198
  ...(!disabled && {
174
199
  onClick: e => {
@@ -204,8 +229,15 @@ const ActionPopoverItem = _ref => {
204
229
  };
205
230
  const renderMenuItemIcon = () => {
206
231
  return icon && /*#__PURE__*/_react.default.createElement(_actionPopover.MenuItemIcon, {
207
- as: undefined,
208
- type: icon
232
+ type: icon,
233
+ "data-element": "action-popover-menu-item-icon",
234
+ horizontalAlignment: horizontalAlignment,
235
+ submenuPosition: currentSubmenuPosition,
236
+ childHasIcon: childHasIcon,
237
+ childHasSubmenu: childHasSubmenu,
238
+ hasIcon: !!icon,
239
+ hasSubmenu: !!submenu,
240
+ isASubmenu: isASubmenu
209
241
  });
210
242
  };
211
243
  return /*#__PURE__*/_react.default.createElement(_actionPopover.StyledMenuItemWrapper, submenu && wrapperDivProps, /*#__PURE__*/_react.default.createElement("div", {
@@ -218,17 +250,31 @@ const ActionPopoverItem = _ref => {
218
250
  role: "menuitem",
219
251
  tabIndex: 0,
220
252
  isDisabled: disabled,
221
- horizontalAlignment: horizontalAlignment
253
+ horizontalAlignment: horizontalAlignment,
254
+ submenuPosition: currentSubmenuPosition,
255
+ hasSubmenu: !!submenu,
256
+ childHasSubmenu: childHasSubmenu
222
257
  }, disabled && {
223
258
  "aria-disabled": true
224
259
  }, isHref && {
225
260
  as: "a",
226
261
  download,
227
262
  href
228
- }, submenu && itemSubmenuProps), submenu && checkRef(ref) && isLeftAligned ? /*#__PURE__*/_react.default.createElement(_actionPopover.SubMenuItemIcon, {
229
- type: "chevron_left"
230
- }) : null, horizontalAlignment === "left" ? renderMenuItemIcon() : null, children, horizontalAlignment === "right" ? renderMenuItemIcon() : null, submenu && checkRef(ref) && !isLeftAligned ? /*#__PURE__*/_react.default.createElement(_actionPopover.SubMenuItemIcon, {
231
- type: "chevron_right"
263
+ }, submenu && itemSubmenuProps), submenu && checkRef(ref) && currentSubmenuPosition === "left" ? /*#__PURE__*/_react.default.createElement(_actionPopover.SubMenuItemIcon, {
264
+ "data-element": "action-popover-menu-item-chevron",
265
+ type: "chevron_left_thick"
266
+ }) : null, /*#__PURE__*/_react.default.createElement(_actionPopover.StyledMenuItemOuterContainer, null, horizontalAlignment === "left" ? renderMenuItemIcon() : null, /*#__PURE__*/_react.default.createElement(_actionPopover.StyledMenuItemInnerText, {
267
+ "data-element": "action-popover-menu-item-inner-text",
268
+ horizontalAlignment: horizontalAlignment,
269
+ submenuPosition: currentSubmenuPosition,
270
+ isASubmenu: isASubmenu,
271
+ childHasSubmenu: childHasSubmenu,
272
+ childHasIcon: childHasIcon,
273
+ hasIcon: !!icon,
274
+ hasSubmenu: !!submenu
275
+ }, children), horizontalAlignment === "right" ? renderMenuItemIcon() : null), submenu && checkRef(ref) && currentSubmenuPosition === "right" ? /*#__PURE__*/_react.default.createElement(_actionPopover.SubMenuItemIcon, {
276
+ "data-element": "action-popover-menu-item-chevron",
277
+ type: "chevron_right_thick"
232
278
  }) : null), /*#__PURE__*/_react.default.isValidElement(submenu) ? /*#__PURE__*/_react.default.cloneElement(submenu, {
233
279
  parentID: `ActionPopoverItem_${guid}`,
234
280
  menuID: `ActionPopoverMenu_${guid}`,
@@ -238,7 +284,9 @@ const ActionPopoverItem = _ref => {
238
284
  style: containerPosition,
239
285
  setOpen,
240
286
  setFocusIndex,
241
- focusIndex
287
+ focusIndex,
288
+ isASubmenu: true,
289
+ horizontalAlignment
242
290
  }) : null));
243
291
  };
244
292
  exports.ActionPopoverItem = ActionPopoverItem;
@@ -1,4 +1,5 @@
1
1
  import React from "react";
2
+ import { Alignment } from "../action-popover-context";
2
3
  export interface ActionPopoverMenuBaseProps {
3
4
  /** Children for the menu */
4
5
  children?: React.ReactNode;
@@ -15,19 +16,21 @@ export interface ActionPopoverMenuBaseProps {
15
16
  /** Unique ID for the menu's parent */
16
17
  parentID?: string;
17
18
  /** Horizontal alignment of menu items content */
18
- horizontalAlignment?: "left" | "right";
19
+ horizontalAlignment?: Alignment;
19
20
  /** Set whether the menu should open above or below the button */
20
21
  placement?: "bottom" | "top";
21
22
  /** @ignore @private */
22
23
  role?: string;
23
24
  /** @ignore @private */
25
+ isASubmenu?: boolean;
26
+ /** @ignore @private */
24
27
  "data-element"?: string;
25
28
  /** @ignore @private */
26
29
  style?: {
27
- left: number;
30
+ left: string | number;
28
31
  top?: string;
29
32
  bottom?: string;
30
- right: "auto";
33
+ right: string | number;
31
34
  };
32
35
  }
33
36
  export interface ActionPopoverMenuProps extends ActionPopoverMenuBaseProps, React.RefAttributes<HTMLDivElement> {
@@ -27,12 +27,14 @@ const ActionPopoverMenu = /*#__PURE__*/_react.default.forwardRef((_ref, ref) =>
27
27
  setFocusIndex,
28
28
  placement = "bottom",
29
29
  horizontalAlignment,
30
+ isASubmenu,
30
31
  ...rest
31
32
  } = _ref;
32
33
  const context = (0, _react.useContext)(_actionPopoverContext.default);
33
34
  !context ? process.env.NODE_ENV !== "production" ? (0, _invariant.default)(false, "ActionPopoverMenu must be used within an ActionPopover component") : (0, _invariant.default)(false) : void 0;
34
35
  const {
35
- focusButton
36
+ focusButton,
37
+ submenuPosition
36
38
  } = context;
37
39
  !(setOpen && setFocusIndex && typeof focusIndex !== "undefined") ? process.env.NODE_ENV !== "production" ? (0, _invariant.default)(false, "ActionPopoverMenu must be used within an ActionPopover or ActionPopoverItem component") : (0, _invariant.default)(false) : void 0;
38
40
  const hasProperChildren = (0, _react.useMemo)(() => {
@@ -101,6 +103,9 @@ const ActionPopoverMenu = /*#__PURE__*/_react.default.forwardRef((_ref, ref) =>
101
103
  }
102
104
  }
103
105
  }, [focusButton, setOpen, focusIndex, items, setFocusIndex]);
106
+ const [childHasSubmenu, setChildHasSubmenu] = (0, _react.useState)(false);
107
+ const [childHasIcon, setChildHasIcon] = (0, _react.useState)(false);
108
+ const [currentSubmenuPosition, setCurrentSubmenuPosition] = (0, _react.useState)(submenuPosition);
104
109
  const clonedChildren = (0, _react.useMemo)(() => {
105
110
  let index = 0;
106
111
  return _react.default.Children.map(children, child => {
@@ -109,12 +114,19 @@ const ActionPopoverMenu = /*#__PURE__*/_react.default.forwardRef((_ref, ref) =>
109
114
  return /*#__PURE__*/_react.default.cloneElement(child, {
110
115
  focusItem: isOpen && focusIndex === index - 1,
111
116
  placement: child.props.submenu ? placement : undefined,
112
- horizontalAlignment
117
+ horizontalAlignment,
118
+ childHasSubmenu,
119
+ setChildHasSubmenu,
120
+ childHasIcon,
121
+ setChildHasIcon,
122
+ currentSubmenuPosition,
123
+ setCurrentSubmenuPosition,
124
+ isASubmenu
113
125
  });
114
126
  }
115
127
  return child;
116
128
  });
117
- }, [children, focusIndex, isOpen, placement, horizontalAlignment]);
129
+ }, [children, focusIndex, isOpen, placement, horizontalAlignment, childHasSubmenu, childHasIcon, currentSubmenuPosition, isASubmenu]);
118
130
  return /*#__PURE__*/_react.default.createElement(_actionPopover.Menu, _extends({
119
131
  "data-component": "action-popover",
120
132
  isOpen: isOpen,
@@ -130,6 +142,7 @@ ActionPopoverMenu.propTypes = {
130
142
  "data-element": _propTypes.default.string,
131
143
  "focusIndex": _propTypes.default.number,
132
144
  "horizontalAlignment": _propTypes.default.oneOf(["left", "right"]),
145
+ "isASubmenu": _propTypes.default.bool,
133
146
  "isOpen": _propTypes.default.bool,
134
147
  "menuID": _propTypes.default.string,
135
148
  "parentID": _propTypes.default.string,
@@ -139,8 +152,8 @@ ActionPopoverMenu.propTypes = {
139
152
  "setOpen": _propTypes.default.func,
140
153
  "style": _propTypes.default.shape({
141
154
  "bottom": _propTypes.default.string,
142
- "left": _propTypes.default.number.isRequired,
143
- "right": _propTypes.default.oneOf(["auto"]).isRequired,
155
+ "left": _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string]).isRequired,
156
+ "right": _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string]).isRequired,
144
157
  "top": _propTypes.default.string
145
158
  })
146
159
  };
@@ -1,5 +1,6 @@
1
1
  import React from "react";
2
2
  import { MarginProps } from "styled-system";
3
+ import { Alignment } from "./action-popover-context";
3
4
  export interface RenderButtonProps {
4
5
  tabIndex: number;
5
6
  "data-element": string;
@@ -14,7 +15,9 @@ export interface ActionPopoverProps extends MarginProps {
14
15
  /** Children for popover component */
15
16
  children?: React.ReactNode;
16
17
  /** Horizontal alignment of menu items content */
17
- horizontalAlignment?: "left" | "right";
18
+ horizontalAlignment?: Alignment;
19
+ /** Sets submenu position */
20
+ submenuPosition?: Alignment;
18
21
  /** Unique ID */
19
22
  id?: string;
20
23
  /** Callback to be called on menu open */
@@ -28,5 +31,5 @@ export interface ActionPopoverProps extends MarginProps {
28
31
  /** Boolean to control whether menu should align to right */
29
32
  rightAlignMenu?: boolean;
30
33
  }
31
- export declare const ActionPopover: ({ children, id, onOpen, onClose, rightAlignMenu, renderButton, placement, horizontalAlignment, ...rest }: ActionPopoverProps) => React.JSX.Element;
34
+ export declare const ActionPopover: ({ children, id, onOpen, onClose, rightAlignMenu, renderButton, placement, horizontalAlignment, submenuPosition, ...rest }: ActionPopoverProps) => React.JSX.Element;
32
35
  export default ActionPopover;
@@ -33,6 +33,7 @@ const ActionPopover = _ref => {
33
33
  renderButton,
34
34
  placement = "bottom",
35
35
  horizontalAlignment = "left",
36
+ submenuPosition = "left",
36
37
  ...rest
37
38
  } = _ref;
38
39
  const l = (0, _useLocale.default)();
@@ -194,6 +195,7 @@ const ActionPopover = _ref => {
194
195
  value: {
195
196
  setOpenPopover: setOpen,
196
197
  focusButton,
198
+ submenuPosition,
197
199
  isOpenPopover: isOpen
198
200
  }
199
201
  }, isOpen && /*#__PURE__*/_react.default.createElement(_popover.default, {
@@ -4,9 +4,17 @@ declare const Menu: import("styled-components").StyledComponent<"div", any, {
4
4
  }, never>;
5
5
  declare type StyledMenuItemProps = {
6
6
  isDisabled: boolean;
7
- horizontalAlignment: "left" | "right";
7
+ horizontalAlignment?: "left" | "right";
8
+ submenuPosition?: "left" | "right";
9
+ childHasSubmenu?: boolean;
10
+ childHasIcon?: boolean;
11
+ hasSubmenu?: boolean;
12
+ hasIcon?: boolean;
13
+ isASubmenu?: boolean;
8
14
  };
9
- declare const StyledMenuItem: import("styled-components").StyledComponent<"button", any, StyledMenuItemProps, never>;
15
+ declare const StyledMenuItemInnerText: import("styled-components").StyledComponent<"div", any, Omit<StyledMenuItemProps, "isDisabled">, never>;
16
+ declare const StyledMenuItemOuterContainer: import("styled-components").StyledComponent<"div", any, {}, never>;
17
+ declare const StyledMenuItem: import("styled-components").StyledComponent<"button", any, Omit<StyledMenuItemProps, "variant">, never>;
10
18
  declare const StyledMenuItemWrapper: import("styled-components").StyledComponent<"div", any, {}, never>;
11
19
  declare const MenuItemDivider: import("styled-components").StyledComponent<"div", any, {
12
20
  "data-element": string;
@@ -14,7 +22,7 @@ declare const MenuItemDivider: import("styled-components").StyledComponent<"div"
14
22
  declare const MenuButton: import("styled-components").StyledComponent<"div", any, {}, never>;
15
23
  declare const ButtonIcon: import("styled-components").StyledComponent<import("react").ForwardRefExoticComponent<import("../icon").IconProps & import("react").RefAttributes<HTMLSpanElement>>, any, {}, never>;
16
24
  declare const StyledButtonIcon: import("styled-components").StyledComponent<"div", any, {}, never>;
17
- declare const MenuItemIcon: import("styled-components").StyledComponent<import("react").ForwardRefExoticComponent<import("../icon").IconProps & import("react").RefAttributes<HTMLSpanElement>>, any, {}, never>;
25
+ declare const MenuItemIcon: import("styled-components").StyledComponent<import("react").ForwardRefExoticComponent<import("../icon").IconProps & import("react").RefAttributes<HTMLSpanElement>>, any, Omit<StyledMenuItemProps, "isDisabled">, never>;
18
26
  declare const SubMenuItemIcon: import("styled-components").StyledComponent<import("react").ForwardRefExoticComponent<import("../icon").IconProps & import("react").RefAttributes<HTMLSpanElement>>, any, {}, never>;
19
27
  declare const MenuButtonOverrideWrapper: import("styled-components").StyledComponent<"div", any, {}, never>;
20
- export { Menu, MenuButton, ButtonIcon, StyledButtonIcon, MenuItemIcon, MenuItemDivider, SubMenuItemIcon, MenuButtonOverrideWrapper, StyledMenuItem, StyledMenuItemWrapper, };
28
+ export { Menu, MenuButton, ButtonIcon, StyledButtonIcon, MenuItemIcon, MenuItemDivider, SubMenuItemIcon, MenuButtonOverrideWrapper, StyledMenuItemInnerText, StyledMenuItemOuterContainer, StyledMenuItem, StyledMenuItemWrapper, };
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.SubMenuItemIcon = exports.StyledMenuItemWrapper = exports.StyledMenuItem = exports.StyledButtonIcon = exports.MenuItemIcon = exports.MenuItemDivider = exports.MenuButtonOverrideWrapper = exports.MenuButton = exports.Menu = exports.ButtonIcon = void 0;
6
+ exports.SubMenuItemIcon = exports.StyledMenuItemWrapper = exports.StyledMenuItemOuterContainer = exports.StyledMenuItemInnerText = exports.StyledMenuItem = exports.StyledButtonIcon = exports.MenuItemIcon = exports.MenuItemDivider = exports.MenuButtonOverrideWrapper = exports.MenuButton = exports.Menu = exports.ButtonIcon = void 0;
7
7
  var _styledComponents = _interopRequireWildcard(require("styled-components"));
8
8
  var _styledSystem = require("styled-system");
9
9
  var _icon = _interopRequireDefault(require("../icon"));
@@ -34,7 +34,87 @@ const Menu = _styledComponents.default.div`
34
34
  }}; // TODO (tokens): implement elevation tokens - FE-4437
35
35
  `;
36
36
  exports.Menu = Menu;
37
+ function getPaddingValues(childHasSubmenu, childHasIcon, hasIcon, hasSubmenu) {
38
+ if (!childHasIcon && childHasSubmenu && !hasIcon && !hasSubmenu) {
39
+ return "var(--spacing400)";
40
+ }
41
+ if (childHasIcon && childHasSubmenu && !hasIcon && hasSubmenu) {
42
+ return "var(--spacing600)";
43
+ }
44
+ if (childHasIcon && childHasSubmenu && !hasIcon && !hasSubmenu) {
45
+ return "var(--spacing900)";
46
+ }
47
+ return "var(--spacing100)";
48
+ }
49
+ function getIconPaddingValues(index, horizontalAlignment, submenuPosition, siblingsHaveIconAndSubmenu, isASubmenu) {
50
+ const sameAlignment = horizontalAlignment === "left" && submenuPosition === "left" || horizontalAlignment === "right" && submenuPosition === "right";
51
+ if (siblingsHaveIconAndSubmenu && sameAlignment) {
52
+ if (horizontalAlignment === "left") {
53
+ return index === 1 ? "var(--spacing100)" : "var(--spacing400)";
54
+ }
55
+ return index === 1 ? "var(--spacing400)" : "var(--spacing100)";
56
+ }
57
+ if (isASubmenu) {
58
+ if (horizontalAlignment === "left") {
59
+ return index === 1 ? "var(--spacing100)" : "var(--spacing000)";
60
+ }
61
+ return index === 1 ? "var(--spacing000)" : "var(--spacing100)";
62
+ }
63
+ return "var(--spacing100)";
64
+ }
65
+ const StyledMenuItemInnerText = _styledComponents.default.div`
66
+ ${_ref3 => {
67
+ let {
68
+ childHasSubmenu,
69
+ childHasIcon,
70
+ hasIcon,
71
+ hasSubmenu,
72
+ submenuPosition,
73
+ horizontalAlignment,
74
+ isASubmenu
75
+ } = _ref3;
76
+ return (0, _styledComponents.css)`
77
+ padding-left: ${isASubmenu ? `var(--spacing000)` : `var(--spacing100)`};
78
+ padding-right: ${isASubmenu ? `var(--spacing000)` : `var(--spacing100)`};
79
+
80
+ ${horizontalAlignment === "left" && submenuPosition === "left" && !isASubmenu && (0, _styledComponents.css)`
81
+ padding-left: ${getPaddingValues(childHasSubmenu, childHasIcon, hasIcon, hasSubmenu)};
82
+ `}
83
+
84
+ ${horizontalAlignment === "right" && submenuPosition === "right" && !isASubmenu && (0, _styledComponents.css)`
85
+ padding-right: ${getPaddingValues(childHasSubmenu, childHasIcon, hasIcon, hasSubmenu)};
86
+ `}
87
+ `;
88
+ }}
89
+ `;
90
+ exports.StyledMenuItemInnerText = StyledMenuItemInnerText;
91
+ const StyledMenuItemOuterContainer = _styledComponents.default.div`
92
+ display: inherit;
93
+ `;
94
+ exports.StyledMenuItemOuterContainer = StyledMenuItemOuterContainer;
37
95
  const StyledMenuItem = _styledComponents.default.button`
96
+ ${_ref4 => {
97
+ let {
98
+ horizontalAlignment,
99
+ submenuPosition,
100
+ childHasSubmenu,
101
+ hasSubmenu
102
+ } = _ref4;
103
+ return (0, _styledComponents.css)`
104
+ justify-content: ${horizontalAlignment === "left" ? "flex-start" : "flex-end"};
105
+
106
+ ${horizontalAlignment === "left" && submenuPosition === "right" && (0, _styledComponents.css)`
107
+ justify-content: space-between;
108
+ `}
109
+
110
+ ${horizontalAlignment === "right" && submenuPosition === "left" && (0, _styledComponents.css)`
111
+ ${childHasSubmenu && hasSubmenu && (0, _styledComponents.css)`
112
+ justify-content: space-between;
113
+ `}
114
+ `}
115
+ `;
116
+ }}
117
+
38
118
  text-decoration: none;
39
119
  background-color: var(--colorsActionMajorYang100);
40
120
  cursor: pointer;
@@ -51,12 +131,6 @@ const StyledMenuItem = _styledComponents.default.button`
51
131
  color: var(--colorsUtilityYin090);
52
132
  font-size: 14px;
53
133
  font-weight: 700;
54
- justify-content: ${_ref3 => {
55
- let {
56
- horizontalAlignment
57
- } = _ref3;
58
- return horizontalAlignment === "left" ? "flex-start" : "flex-end";
59
- }};
60
134
 
61
135
  &:focus {
62
136
  outline: var(--borderWidth300) solid var(--colorsSemanticFocus500);
@@ -64,10 +138,10 @@ const StyledMenuItem = _styledComponents.default.button`
64
138
  border-radius: var(--borderRadius000);
65
139
  }
66
140
 
67
- ${_ref4 => {
141
+ ${_ref5 => {
68
142
  let {
69
143
  isDisabled
70
- } = _ref4;
144
+ } = _ref5;
71
145
  return isDisabled && (0, _styledComponents.css)`
72
146
  color: var(--colorsUtilityYin030);
73
147
  cursor: not-allowed;
@@ -79,10 +153,10 @@ const StyledMenuItem = _styledComponents.default.button`
79
153
  `;
80
154
  }}
81
155
 
82
- ${_ref5 => {
156
+ ${_ref6 => {
83
157
  let {
84
158
  isDisabled
85
- } = _ref5;
159
+ } = _ref6;
86
160
  return !isDisabled && (0, _styledComponents.css)`
87
161
  &:focus,
88
162
  &:hover {
@@ -133,22 +207,38 @@ const StyledButtonIcon = _styledComponents.default.div`
133
207
  `;
134
208
  exports.StyledButtonIcon = StyledButtonIcon;
135
209
  const MenuItemIcon = (0, _styledComponents.default)(_icon.default)`
136
- padding: var(--spacing100);
137
- color: var(--colorsUtilityYin065);
210
+ ${_ref7 => {
211
+ let {
212
+ horizontalAlignment,
213
+ submenuPosition,
214
+ childHasIcon,
215
+ childHasSubmenu,
216
+ hasIcon,
217
+ hasSubmenu,
218
+ isASubmenu
219
+ } = _ref7;
220
+ return (0, _styledComponents.css)`
221
+ justify-content: ${horizontalAlignment};
222
+ padding: var(--spacing100)
223
+ ${getIconPaddingValues(1, horizontalAlignment, submenuPosition, childHasIcon && childHasSubmenu && hasIcon && !hasSubmenu, isASubmenu)}
224
+ var(--spacing100)
225
+ ${getIconPaddingValues(2, horizontalAlignment, submenuPosition, childHasIcon && childHasSubmenu && hasIcon && !hasSubmenu, isASubmenu)};
226
+ color: var(--colorsUtilityYin065);
227
+ `;
228
+ }}
138
229
  `;
139
230
  exports.MenuItemIcon = MenuItemIcon;
140
231
  const SubMenuItemIcon = (0, _styledComponents.default)(ButtonIcon)`
141
- ${_ref6 => {
232
+ ${_ref8 => {
142
233
  let {
143
234
  type
144
- } = _ref6;
235
+ } = _ref8;
145
236
  return (0, _styledComponents.css)`
146
- position: absolute;
147
- ${type === "chevron_left" && (0, _styledComponents.css)`
148
- left: -2px;
237
+ ${type === "chevron_left_thick" && (0, _styledComponents.css)`
238
+ left: -5px;
149
239
  `}
150
240
 
151
- ${type === "chevron_right" && (0, _styledComponents.css)`
241
+ ${type === "chevron_right_thick" && (0, _styledComponents.css)`
152
242
  right: -5px;
153
243
  ${(0, _browserTypeCheck.isSafari)(navigator) && (0, _styledComponents.css)`
154
244
  top: var(--sizing100);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "carbon-react",
3
- "version": "119.8.0",
3
+ "version": "119.9.0",
4
4
  "description": "A library of reusable React components for easily building user interfaces.",
5
5
  "files": [
6
6
  "lib",