@reltio/components 1.4.2302 → 1.4.2312

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 (86) hide show
  1. package/DropDownMenuButton/DropDownMenuButton.d.ts +28 -8
  2. package/DropDownMenuButton/DropDownMenuButton.js +89 -30
  3. package/DropDownMenuButton/DropDownMenuButton.module.css.js +9 -0
  4. package/DropDownMenuButton/index.d.ts +12 -1
  5. package/DropDownMenuButton/index.js +47 -1
  6. package/DropDownMenuButton/legacy/DropDownMenuButton.d.ts +16 -0
  7. package/DropDownMenuButton/legacy/DropDownMenuButton.js +59 -0
  8. package/DropDownMenuButton/{DropDownMenuButton.spec.js → legacy/DropDownMenuButton.spec.js} +1 -1
  9. package/RelationEditor/RelationEditor.js +7 -9
  10. package/RelationEditor/RelationEditor.module.css.js +9 -0
  11. package/VerificationButton/VerificationButton.js +3 -2
  12. package/cjs/DropDownMenuButton/DropDownMenuButton.d.ts +28 -8
  13. package/cjs/DropDownMenuButton/DropDownMenuButton.js +90 -28
  14. package/cjs/DropDownMenuButton/DropDownMenuButton.module.css.js +9 -0
  15. package/cjs/DropDownMenuButton/index.d.ts +12 -1
  16. package/cjs/DropDownMenuButton/index.js +50 -2
  17. package/cjs/DropDownMenuButton/legacy/DropDownMenuButton.d.ts +16 -0
  18. package/cjs/DropDownMenuButton/legacy/DropDownMenuButton.js +88 -0
  19. package/cjs/DropDownMenuButton/{DropDownMenuButton.spec.js → legacy/DropDownMenuButton.spec.js} +1 -1
  20. package/cjs/RelationEditor/RelationEditor.js +11 -13
  21. package/cjs/RelationEditor/RelationEditor.module.css.js +9 -0
  22. package/cjs/VerificationButton/VerificationButton.js +3 -2
  23. package/cjs/contexts/DesignMigrationContext/index.d.ts +8 -0
  24. package/cjs/contexts/DesignMigrationContext/index.js +35 -0
  25. package/cjs/features/history/ProfileBandHistory/ProfileBandHistory.js +6 -8
  26. package/cjs/features/history/ProfileBandHistory/ProfileBandHistory.module.css.js +9 -0
  27. package/cjs/features/history/ProfileBandHistory/index.d.ts +2 -1
  28. package/cjs/features/history/ProfileBandHistory/index.js +12 -2
  29. package/cjs/features/history/ProfileBandHistory/legacy/ProfileBandHistory.d.ts +2 -0
  30. package/cjs/features/history/ProfileBandHistory/legacy/ProfileBandHistory.js +24 -0
  31. package/cjs/features/history/ProfileBandHistory/{ProfileBandHistory.test.js → legacy/ProfileBandHistory.test.js} +1 -1
  32. package/cjs/features/workflow/AddWorkflowButton/AddWorkflowButton.js +4 -7
  33. package/cjs/features/workflow/AddWorkflowButton/AddWorkflowButton.module.css.js +9 -0
  34. package/cjs/helpers/errors.js +3 -0
  35. package/cjs/helpers/errors.test.js +4 -0
  36. package/cjs/hooks/useReadableSearchState/useReadableSearchState.js +6 -0
  37. package/cjs/hooks/useReadableSearchState/useReadableSearchState.test.js +30 -2
  38. package/contexts/DesignMigrationContext/index.d.ts +8 -0
  39. package/contexts/DesignMigrationContext/index.js +7 -0
  40. package/features/history/HistoryRow/styles.d.ts +1 -1
  41. package/features/history/ProfileBandHistory/ProfileBandHistory.js +4 -6
  42. package/features/history/ProfileBandHistory/ProfileBandHistory.module.css.js +9 -0
  43. package/features/history/ProfileBandHistory/index.d.ts +2 -1
  44. package/features/history/ProfileBandHistory/index.js +8 -1
  45. package/features/history/ProfileBandHistory/legacy/ProfileBandHistory.d.ts +2 -0
  46. package/features/history/ProfileBandHistory/legacy/ProfileBandHistory.js +17 -0
  47. package/features/history/ProfileBandHistory/{ProfileBandHistory.test.js → legacy/ProfileBandHistory.test.js} +1 -1
  48. package/features/workflow/AddWorkflowButton/AddWorkflowButton.js +4 -7
  49. package/features/workflow/AddWorkflowButton/AddWorkflowButton.module.css.js +9 -0
  50. package/helpers/errors.js +4 -1
  51. package/helpers/errors.test.js +4 -0
  52. package/hooks/useReadableSearchState/useReadableSearchState.js +6 -0
  53. package/hooks/useReadableSearchState/useReadableSearchState.test.js +30 -2
  54. package/package.json +3 -3
  55. package/RelationEditor/styles.d.ts +0 -1
  56. package/RelationEditor/styles.js +0 -34
  57. package/cjs/RelationEditor/styles.d.ts +0 -1
  58. package/cjs/RelationEditor/styles.js +0 -37
  59. package/cjs/features/workflow/AddWorkflowButton/styles.d.ts +0 -1
  60. package/cjs/features/workflow/AddWorkflowButton/styles.js +0 -20
  61. package/features/workflow/AddWorkflowButton/styles.d.ts +0 -1
  62. package/features/workflow/AddWorkflowButton/styles.js +0 -17
  63. /package/DropDownMenuButton/{components → legacy}/DefaultMenuItemRenderer/DefaultMenuItemRenderer.d.ts +0 -0
  64. /package/DropDownMenuButton/{components → legacy}/DefaultMenuItemRenderer/DefaultMenuItemRenderer.js +0 -0
  65. /package/DropDownMenuButton/{components → legacy}/DefaultMenuItemRenderer/index.d.ts +0 -0
  66. /package/DropDownMenuButton/{components → legacy}/DefaultMenuItemRenderer/index.js +0 -0
  67. /package/DropDownMenuButton/{components → legacy}/DefaultMenuItemRenderer/styles.d.ts +0 -0
  68. /package/DropDownMenuButton/{components → legacy}/DefaultMenuItemRenderer/styles.js +0 -0
  69. /package/DropDownMenuButton/{DropDownMenuButton.spec.d.ts → legacy/DropDownMenuButton.spec.d.ts} +0 -0
  70. /package/DropDownMenuButton/{styles.d.ts → legacy/styles.d.ts} +0 -0
  71. /package/DropDownMenuButton/{styles.js → legacy/styles.js} +0 -0
  72. /package/cjs/DropDownMenuButton/{components → legacy}/DefaultMenuItemRenderer/DefaultMenuItemRenderer.d.ts +0 -0
  73. /package/cjs/DropDownMenuButton/{components → legacy}/DefaultMenuItemRenderer/DefaultMenuItemRenderer.js +0 -0
  74. /package/cjs/DropDownMenuButton/{components → legacy}/DefaultMenuItemRenderer/index.d.ts +0 -0
  75. /package/cjs/DropDownMenuButton/{components → legacy}/DefaultMenuItemRenderer/index.js +0 -0
  76. /package/cjs/DropDownMenuButton/{components → legacy}/DefaultMenuItemRenderer/styles.d.ts +0 -0
  77. /package/cjs/DropDownMenuButton/{components → legacy}/DefaultMenuItemRenderer/styles.js +0 -0
  78. /package/cjs/DropDownMenuButton/{DropDownMenuButton.spec.d.ts → legacy/DropDownMenuButton.spec.d.ts} +0 -0
  79. /package/cjs/DropDownMenuButton/{styles.d.ts → legacy/styles.d.ts} +0 -0
  80. /package/cjs/DropDownMenuButton/{styles.js → legacy/styles.js} +0 -0
  81. /package/cjs/features/history/ProfileBandHistory/{ProfileBandHistory.test.d.ts → legacy/ProfileBandHistory.test.d.ts} +0 -0
  82. /package/cjs/features/history/ProfileBandHistory/{styles.d.ts → legacy/styles.d.ts} +0 -0
  83. /package/cjs/features/history/ProfileBandHistory/{styles.js → legacy/styles.js} +0 -0
  84. /package/features/history/ProfileBandHistory/{ProfileBandHistory.test.d.ts → legacy/ProfileBandHistory.test.d.ts} +0 -0
  85. /package/features/history/ProfileBandHistory/{styles.d.ts → legacy/styles.d.ts} +0 -0
  86. /package/features/history/ProfileBandHistory/{styles.js → legacy/styles.js} +0 -0
@@ -1,17 +1,37 @@
1
1
  import React from 'react';
2
- import { MenuProps } from '@mui/material/Menu';
3
- import { DropDownMenuItem } from '../types';
4
- type Props<T> = {
2
+ export type DropDownMenuItem = {
3
+ text?: string;
4
+ id?: string;
5
+ /** A SAP icon name. */
6
+ icon?: string;
7
+ additionalText?: string;
8
+ tooltip?: string;
9
+ disabled?: boolean;
10
+ /** Only rendered inside a group, and controlled: the mark follows this prop, nothing else. */
11
+ checked?: boolean;
12
+ onClick?: (e: any) => void;
13
+ items?: DropDownMenuNode[];
14
+ };
15
+ export type DropDownMenuGroup = {
16
+ checkMode: 'Single' | 'Multiple';
17
+ items: DropDownMenuItem[];
18
+ };
19
+ export type DropDownMenuSeparator = {
20
+ separator: true;
21
+ };
22
+ export type DropDownMenuNode = DropDownMenuItem | DropDownMenuGroup | DropDownMenuSeparator;
23
+ /** Narrower than the legacy contract: `popoverProps` and `MenuItemRenderer` are MUI-only. */
24
+ export type DropDownMenuButtonProps = {
5
25
  className?: string;
6
26
  buttonComponent?: React.ElementType;
7
27
  buttonContent?: React.ReactNode;
8
- menuItems?: T[];
28
+ menuItems?: DropDownMenuNode[];
9
29
  buttonProps?: Record<string, unknown>;
10
- popoverProps?: Omit<MenuProps, 'open'>;
11
30
  menuId: string;
31
+ horizontalAlign?: 'Start' | 'Center' | 'End';
12
32
  onMenuOpen?: () => void;
13
33
  onMenuClose?: () => void;
14
- MenuItemRenderer?: (props: any) => React.ReactNode;
15
34
  };
16
- export declare function DropDownMenuButton<T = DropDownMenuItem>({ className, menuId, buttonComponent, buttonContent, menuItems, buttonProps, popoverProps, onMenuOpen, onMenuClose, MenuItemRenderer }: Props<T>): React.JSX.Element;
17
- export {};
35
+ export declare const isSeparatorNode: (node: DropDownMenuNode) => node is DropDownMenuSeparator;
36
+ export declare const isGroupNode: (node: DropDownMenuNode) => node is DropDownMenuGroup;
37
+ export declare function DropDownMenuButton({ className, menuId, buttonComponent, buttonContent, menuItems, buttonProps, horizontalAlign, onMenuOpen, onMenuClose }: DropDownMenuButtonProps): React.JSX.Element;
@@ -9,51 +9,110 @@ var __assign = (this && this.__assign) || function () {
9
9
  };
10
10
  return __assign.apply(this, arguments);
11
11
  };
12
- import React, { useCallback, useRef, useState } from 'react';
12
+ import React, { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';
13
13
  import classnames from 'classnames';
14
14
  import { identity } from 'ramda';
15
- import Button from '@mui/material/Button';
16
- import Menu from '@mui/material/Menu';
17
- import { DefaultMenuItemRenderer } from './components/DefaultMenuItemRenderer';
15
+ import { Button, Menu, MenuItem, MenuItemGroup, MenuSeparator, Tooltip } from '@reltio/design/components';
18
16
  import { useDidUpdateEffect } from '../hooks/useDidUpdateEffect';
19
- import { useStyles } from './styles';
17
+ import styles from './DropDownMenuButton.module.css';
20
18
  var EMPTY_ARRAY = [];
21
19
  var EMPTY_OBJECT = {};
20
+ export var isSeparatorNode = function (node) { return 'separator' in node; };
21
+ export var isGroupNode = function (node) { return 'checkMode' in node; };
22
+ var buildMenu = function (nodes) {
23
+ var handlers = new Map();
24
+ var renderNode = function (node, key, checkMode) {
25
+ var _a, _b;
26
+ if (isSeparatorNode(node)) {
27
+ return React.createElement(MenuSeparator, { key: key });
28
+ }
29
+ if (isGroupNode(node)) {
30
+ return (React.createElement(MenuItemGroup, { key: key, checkMode: node.checkMode }, node.items.map(function (item, index) { return renderNode(item, "".concat(key, ".").concat(index), node.checkMode); })));
31
+ }
32
+ handlers.set(key, { item: node, checkMode: checkMode });
33
+ return (React.createElement(Tooltip, { key: key, title: node.tooltip },
34
+ React.createElement(MenuItem, { "data-item-key": key, "data-reltio-id": "reltio-search-menu-item".concat((_a = node.id) !== null && _a !== void 0 ? _a : ''),
35
+ // The wrapper describes the host, while the role sits on the shadow item, so the
36
+ // text has to come in as a name. The native `tooltip` would reach it too, but as
37
+ // a `title` that draws a second, unstyled bubble.
38
+ accessibleName: node.tooltip && [node.text, node.tooltip].filter(Boolean).join('. '), text: node.text, icon: node.icon, additionalText: node.additionalText, disabled: node.disabled, checked: node.checked }, (_b = node.items) === null || _b === void 0 ? void 0 : _b.map(function (child, index) { return renderNode(child, "".concat(key, ".").concat(index)); }))));
39
+ };
40
+ return { children: nodes.map(function (node, index) { return renderNode(node, String(index)); }), handlers: handlers };
41
+ };
22
42
  export function DropDownMenuButton(_a) {
23
43
  var _b;
24
- var className = _a.className, menuId = _a.menuId, _c = _a.buttonComponent, buttonComponent = _c === void 0 ? Button : _c, buttonContent = _a.buttonContent, _d = _a.menuItems, menuItems = _d === void 0 ? EMPTY_ARRAY : _d, _e = _a.buttonProps, buttonProps = _e === void 0 ? EMPTY_OBJECT : _e, _f = _a.popoverProps, popoverProps = _f === void 0 ? EMPTY_OBJECT : _f, _g = _a.onMenuOpen, onMenuOpen = _g === void 0 ? identity : _g, _h = _a.onMenuClose, onMenuClose = _h === void 0 ? identity : _h, _j = _a.MenuItemRenderer, MenuItemRenderer = _j === void 0 ? DefaultMenuItemRenderer : _j;
44
+ var _c;
45
+ var className = _a.className, menuId = _a.menuId, _d = _a.buttonComponent, buttonComponent = _d === void 0 ? Button : _d, buttonContent = _a.buttonContent, _e = _a.menuItems, menuItems = _e === void 0 ? EMPTY_ARRAY : _e, _f = _a.buttonProps, buttonProps = _f === void 0 ? EMPTY_OBJECT : _f, _g = _a.horizontalAlign, horizontalAlign = _g === void 0 ? 'End' : _g, _h = _a.onMenuOpen, onMenuOpen = _h === void 0 ? identity : _h, _j = _a.onMenuClose, onMenuClose = _j === void 0 ? identity : _j;
25
46
  var ButtonComponent = buttonComponent;
26
- var styles = useStyles();
27
- var buttonRef = useRef(null);
47
+ // The menu resolves the opener id on every use, so it survives a button node being replaced,
48
+ // which a ref captured in render would not. menuId is not unique on its own — the same menu is
49
+ // rendered per row in a few places.
50
+ var instanceId = useId();
51
+ var openerId = (_c = buttonProps.id) !== null && _c !== void 0 ? _c : "".concat(menuId, "-opener").concat(instanceId);
28
52
  var _k = useState(false), isOpen = _k[0], setIsOpen = _k[1];
29
- // onMenuOpen is fired synchronously in the click handler (below) so it wins the race against a
30
- // parent's onMouseLeave when the button lives in an unmounting hover overlay; onMenuClose stays
31
- // deferred to this effect so consumers can keep the button mounted (e.g. delete confirmation).
53
+ // onMenuOpen fires synchronously in the click handler to beat a parent's onMouseLeave in an
54
+ // unmounting hover overlay; onMenuClose stays deferred so consumers can keep the button mounted.
32
55
  useDidUpdateEffect(function () {
33
56
  if (!isOpen) {
34
57
  onMenuClose();
35
58
  }
36
59
  }, [isOpen]);
60
+ var closeMenu = useCallback(function () { return setIsOpen(false); }, []);
37
61
  var handleToggle = useCallback(function (e) {
38
62
  e === null || e === void 0 ? void 0 : e.stopPropagation();
39
- var nextIsOpen = !isOpen;
40
- setIsOpen(nextIsOpen);
41
- if (nextIsOpen) {
42
- onMenuOpen();
43
- }
44
- }, [isOpen, onMenuOpen]);
45
- var renderMenuItem = function (item, index) {
46
- return React.createElement(MenuItemRenderer, { item: item, key: index, onMenuClose: handleToggle });
47
- };
63
+ if (isOpen) {
64
+ closeMenu();
65
+ return;
66
+ }
67
+ setIsOpen(true);
68
+ onMenuOpen();
69
+ }, [isOpen, closeMenu, onMenuOpen]);
70
+ // UI5 hands focus back to the opener while closing, so an item runs once that is over. Otherwise
71
+ // anything it focuses itself, such as a dialog without a focus trap, loses the focus right after.
72
+ var pendingClick = useRef(null);
73
+ var flushPendingClick = useCallback(function () {
74
+ var click = pendingClick.current;
75
+ pendingClick.current = null;
76
+ click === null || click === void 0 ? void 0 : click();
77
+ }, []);
78
+ // The popover closes a render later, so an opener that unmounts meanwhile — a hover overlay,
79
+ // for one — would otherwise swallow the action.
80
+ useEffect(function () { return flushPendingClick; }, [flushPendingClick]);
81
+ var handleMenuClose = useCallback(function () {
82
+ setIsOpen(false);
83
+ flushPendingClick();
84
+ }, [flushPendingClick]);
85
+ var _l = useMemo(function () { return buildMenu(menuItems); }, [menuItems]), children = _l.children, handlers = _l.handlers;
86
+ // A UI5 button builds the ARIA of its shadow <button> from this property; host attributes never
87
+ // reach it. `controls` is left out on purpose: the id would have to resolve from inside the shadow
88
+ // root, and an IDREF does not cross that boundary.
89
+ var accessibilityAttributes = useMemo(function () { return ({ hasPopup: 'menu', expanded: isOpen }); }, [isOpen]);
90
+ // UI5 reports the click on the root menu, submenus included, so the handler is looked up by key.
91
+ var handleItemClick = useCallback(function (e) {
92
+ var _a;
93
+ var entry = handlers.get(e.detail.item.dataset.itemKey);
94
+ if (!entry) {
95
+ return;
96
+ }
97
+ var item = entry.item, checkMode = entry.checkMode;
98
+ // Left alone, UI5 flips the mark itself and drifts away from the props. Cancelling the
99
+ // event takes both the mark and the closing back, at the cost of owning them here.
100
+ if (checkMode) {
101
+ e.preventDefault();
102
+ }
103
+ // A Multiple group is the one place where the menu stays open, so nothing has to wait.
104
+ if (checkMode === 'Multiple') {
105
+ (_a = item.onClick) === null || _a === void 0 ? void 0 : _a.call(item, e);
106
+ return;
107
+ }
108
+ if (item.onClick) {
109
+ pendingClick.current = function () { return item.onClick(e); };
110
+ }
111
+ if (checkMode === 'Single') {
112
+ closeMenu();
113
+ }
114
+ }, [handlers, closeMenu]);
48
115
  return (React.createElement(React.Fragment, null,
49
- React.createElement(ButtonComponent, __assign({ "data-reltio-id": "drop-down-menu-button", ref: buttonRef, className: classnames(styles.buttonRoot, (_b = {},
50
- _b[styles.selected] = isOpen,
51
- _b), className), onClick: handleToggle, "aria-pressed": isOpen, "aria-controls": isOpen ? menuId : undefined, "aria-haspopup": "true" }, buttonProps), buttonContent),
52
- React.createElement(Menu, __assign({ id: menuId, variant: "menu", open: isOpen, autoFocus: false, classes: { paper: styles.paper }, anchorEl: buttonRef.current, onClose: handleToggle, anchorOrigin: {
53
- vertical: 'bottom',
54
- horizontal: 'right'
55
- }, transformOrigin: {
56
- vertical: 'top',
57
- horizontal: 'right'
58
- }, elevation: 2, MenuListProps: { autoFocusItem: isOpen } }, popoverProps), menuItems.map(renderMenuItem))));
116
+ React.createElement(ButtonComponent, __assign({ "data-reltio-id": "drop-down-menu-button", id: openerId, className: classnames((_b = {}, _b[styles.open] = isOpen, _b), className), onClick: handleToggle, accessibilityAttributes: accessibilityAttributes }, buttonProps), buttonContent),
117
+ React.createElement(Menu, { id: menuId, open: isOpen, opener: openerId, onClose: handleMenuClose, onItemClick: handleItemClick, horizontalAlign: horizontalAlign }, children)));
59
118
  }
@@ -0,0 +1,9 @@
1
+ const styles = {"open":"DropDownMenuButton-open--2Amub"};
2
+ if (typeof document !== 'undefined') {
3
+ const head = document.head || document.getElementsByTagName('head')[0]
4
+ const style = document.createElement('style');
5
+ style.type = 'text/css'
6
+ style.innerHTML = `.DropDownMenuButton-open--2Amub{--sapButton_Lite_Background:var(--sapButton_Selected_Background);--sapButton_Lite_BorderColor:var(--sapButton_Selected_BorderColor);--sapButton_Lite_TextColor:var(--sapButton_Selected_TextColor);--sapButton_Lite_Hover_Background:var(--sapButton_Selected_Hover_Background);--sapButton_Lite_Hover_BorderColor:var(--sapButton_Selected_Hover_BorderColor);--sapButton_Lite_Hover_TextColor:var(--sapButton_Selected_TextColor);--sapButton_Background:var(--sapButton_Selected_Background);--sapButton_BorderColor:var(--sapButton_Selected_BorderColor);--sapButton_TextColor:var(--sapButton_Selected_TextColor);--sapButton_Hover_Background:var(--sapButton_Selected_Hover_Background);--sapButton_Hover_BorderColor:var(--sapButton_Selected_Hover_BorderColor);--sapButton_Hover_TextColor:var(--sapButton_Selected_TextColor)}`;
7
+ head.appendChild(style);
8
+ }
9
+ export default styles;
@@ -1 +1,12 @@
1
- export { DropDownMenuButton } from './DropDownMenuButton';
1
+ import React from 'react';
2
+ import { DropDownMenuItem as LegacyDropDownMenuItem } from '../types';
3
+ import { DropDownMenuButtonProps as LegacyProps } from './legacy/DropDownMenuButton';
4
+ export type { DropDownMenuGroup, DropDownMenuItem, DropDownMenuNode, DropDownMenuSeparator } from './DropDownMenuButton';
5
+ /**
6
+ * The legacy contract plus what only the design rendering understands. `horizontalAlign` has no
7
+ * legacy counterpart, so it does nothing until the call site routes to the design implementation.
8
+ */
9
+ type DropDownMenuButtonProps<T> = LegacyProps<T> & {
10
+ horizontalAlign?: 'Start' | 'Center' | 'End';
11
+ };
12
+ export declare function DropDownMenuButton<T = LegacyDropDownMenuItem>(props: DropDownMenuButtonProps<T>): React.JSX.Element;
@@ -1 +1,47 @@
1
- export { DropDownMenuButton } from './DropDownMenuButton';
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
12
+ import React from 'react';
13
+ import { useDesignMigration } from '../contexts/DesignMigrationContext';
14
+ import { DropDownMenuButton as DesignDropDownMenuButton, isGroupNode, isSeparatorNode } from './DropDownMenuButton';
15
+ import { DropDownMenuButton as LegacyDropDownMenuButton } from './legacy/DropDownMenuButton';
16
+ var isRenderableByDesign = function (nodes) {
17
+ if (nodes === void 0) { nodes = []; }
18
+ return nodes.every(function (node) {
19
+ if (isSeparatorNode(node)) {
20
+ return true;
21
+ }
22
+ if (isGroupNode(node)) {
23
+ return isRenderableByDesign(node.items);
24
+ }
25
+ return (
26
+ // Legacy sugar for a checkbox. The design rendering wants an explicit group instead,
27
+ // because in UI5 being checkable is a trait of the group rather than of the item.
28
+ !node.selectable &&
29
+ // A React component icon means an unmigrated call site: UI5 takes SAP icon names.
30
+ (node.icon === undefined || typeof node.icon === 'string') &&
31
+ isRenderableByDesign(node.items));
32
+ });
33
+ };
34
+ /**
35
+ * A UI5 menu only takes UI5 `MenuItem`s, so call sites rendering their own MUI items or tuning the
36
+ * MUI popover keep the legacy rendering even inside a migrated screen.
37
+ */
38
+ var isSupportedByDesign = function (_a) {
39
+ var MenuItemRenderer = _a.MenuItemRenderer, popoverProps = _a.popoverProps, menuItems = _a.menuItems;
40
+ return !MenuItemRenderer && !popoverProps && isRenderableByDesign(menuItems);
41
+ };
42
+ export function DropDownMenuButton(props) {
43
+ if (useDesignMigration() && isSupportedByDesign(props)) {
44
+ return React.createElement(DesignDropDownMenuButton, __assign({}, props, { menuItems: props.menuItems }));
45
+ }
46
+ return React.createElement(LegacyDropDownMenuButton, __assign({}, props));
47
+ }
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import { MenuProps } from '@mui/material/Menu';
3
+ import { DropDownMenuItem } from '../../types';
4
+ export type DropDownMenuButtonProps<T> = {
5
+ className?: string;
6
+ buttonComponent?: React.ElementType;
7
+ buttonContent?: React.ReactNode;
8
+ menuItems?: T[];
9
+ buttonProps?: Record<string, unknown>;
10
+ popoverProps?: Omit<MenuProps, 'open'>;
11
+ menuId: string;
12
+ onMenuOpen?: () => void;
13
+ onMenuClose?: () => void;
14
+ MenuItemRenderer?: (props: any) => React.ReactNode;
15
+ };
16
+ export declare function DropDownMenuButton<T = DropDownMenuItem>({ className, menuId, buttonComponent, buttonContent, menuItems, buttonProps, popoverProps, onMenuOpen, onMenuClose, MenuItemRenderer }: DropDownMenuButtonProps<T>): React.JSX.Element;
@@ -0,0 +1,59 @@
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
12
+ import React, { useCallback, useRef, useState } from 'react';
13
+ import classnames from 'classnames';
14
+ import { identity } from 'ramda';
15
+ import Button from '@mui/material/Button';
16
+ import Menu from '@mui/material/Menu';
17
+ import { DefaultMenuItemRenderer } from './DefaultMenuItemRenderer';
18
+ import { useDidUpdateEffect } from '../../hooks/useDidUpdateEffect';
19
+ import { useStyles } from './styles';
20
+ var EMPTY_ARRAY = [];
21
+ var EMPTY_OBJECT = {};
22
+ export function DropDownMenuButton(_a) {
23
+ var _b;
24
+ var className = _a.className, menuId = _a.menuId, _c = _a.buttonComponent, buttonComponent = _c === void 0 ? Button : _c, buttonContent = _a.buttonContent, _d = _a.menuItems, menuItems = _d === void 0 ? EMPTY_ARRAY : _d, _e = _a.buttonProps, buttonProps = _e === void 0 ? EMPTY_OBJECT : _e, _f = _a.popoverProps, popoverProps = _f === void 0 ? EMPTY_OBJECT : _f, _g = _a.onMenuOpen, onMenuOpen = _g === void 0 ? identity : _g, _h = _a.onMenuClose, onMenuClose = _h === void 0 ? identity : _h, _j = _a.MenuItemRenderer, MenuItemRenderer = _j === void 0 ? DefaultMenuItemRenderer : _j;
25
+ var ButtonComponent = buttonComponent;
26
+ var styles = useStyles();
27
+ var buttonRef = useRef(null);
28
+ var _k = useState(false), isOpen = _k[0], setIsOpen = _k[1];
29
+ // onMenuOpen is fired synchronously in the click handler (below) so it wins the race against a
30
+ // parent's onMouseLeave when the button lives in an unmounting hover overlay; onMenuClose stays
31
+ // deferred to this effect so consumers can keep the button mounted (e.g. delete confirmation).
32
+ useDidUpdateEffect(function () {
33
+ if (!isOpen) {
34
+ onMenuClose();
35
+ }
36
+ }, [isOpen]);
37
+ var handleToggle = useCallback(function (e) {
38
+ e === null || e === void 0 ? void 0 : e.stopPropagation();
39
+ var nextIsOpen = !isOpen;
40
+ setIsOpen(nextIsOpen);
41
+ if (nextIsOpen) {
42
+ onMenuOpen();
43
+ }
44
+ }, [isOpen, onMenuOpen]);
45
+ var renderMenuItem = function (item, index) {
46
+ return React.createElement(MenuItemRenderer, { item: item, key: index, onMenuClose: handleToggle });
47
+ };
48
+ return (React.createElement(React.Fragment, null,
49
+ React.createElement(ButtonComponent, __assign({ "data-reltio-id": "drop-down-menu-button", ref: buttonRef, className: classnames(styles.buttonRoot, (_b = {},
50
+ _b[styles.selected] = isOpen,
51
+ _b), className), onClick: handleToggle, "aria-pressed": isOpen, "aria-controls": isOpen ? menuId : undefined, "aria-haspopup": "true" }, buttonProps), buttonContent),
52
+ React.createElement(Menu, __assign({ id: menuId, variant: "menu", open: isOpen, autoFocus: false, classes: { paper: styles.paper }, anchorEl: buttonRef.current, onClose: handleToggle, anchorOrigin: {
53
+ vertical: 'bottom',
54
+ horizontal: 'right'
55
+ }, transformOrigin: {
56
+ vertical: 'top',
57
+ horizontal: 'right'
58
+ }, elevation: 2, MenuListProps: { autoFocusItem: isOpen } }, popoverProps), menuItems.map(renderMenuItem))));
59
+ }
@@ -59,7 +59,7 @@ var __rest = (this && this.__rest) || function (s, e) {
59
59
  import React from 'react';
60
60
  import { render, screen, within } from '@testing-library/react';
61
61
  import userEvent from '@testing-library/user-event';
62
- import { DropDownMenuButton } from '../DropDownMenuButton';
62
+ import { DropDownMenuButton } from './DropDownMenuButton';
63
63
  var menuItems = [
64
64
  { text: 'Disabled custom action', disabled: true, tooltip: 'Disabled custom tooltip', onClick: jest.fn() },
65
65
  { text: 'Custom action', disabled: false, tooltip: 'Custom tooltip', onClick: jest.fn() }
@@ -13,8 +13,7 @@ import React, { useCallback, useEffect, useMemo, useRef } from 'react';
13
13
  import i18n from 'ui-i18n';
14
14
  import classnames from 'classnames';
15
15
  import { always, assoc, concat, differenceWith, equals, forEach, map, path, pick, pipe, prop, propEq, when, uniq, curry, values, flatten, any } from 'ramda';
16
- import Button from '@mui/material/Button';
17
- import Typography from '@mui/material/Typography';
16
+ import { Button, Text } from '@reltio/design/components';
18
17
  import { createTemporaryEntity, Directions, findRelatedTemporaryEntities, getActivenessAttributes, getCreatableAttributeTypes, getEntityType, getErrorMessage, getErrorId, getRelationType, getSuitableEntityTypeUrisForRelationTypes, isAvailableRelationBetweenEntities, omitMarks, relationEditorAttributeTypesSelectionStrategy, validateConnection, updateConnectionEntityLabel, isTempUri, isEmptyValue, getRelationAttributesList, getDefaultRelationTypeObject, getInOutRelations, isSuggestMode, isAttributeTypeError, getRelationUriFromConnection, getRelationEditorActiveError, getRelationAttributesFromConnection, getEntityUriFromConnection, evaluateErrorPath, AttributeValuePathItemType, getAllRelationEditorActiveErrors, isAttributeHasMaskedValue } from '@reltio/mdm-sdk';
19
18
  import { ConnectionRelationTypeSelector } from '../ConnectionRelationTypeSelector';
20
19
  import { ErrorWrapper } from '../ErrorWrapper';
@@ -27,7 +26,7 @@ import { useMdmAction, useMdmAuthoringItemsByEntityType, useMdmEntity, useMdmGlo
27
26
  import { HiddenAttributesContext } from '../contexts/HiddenAttributesContext';
28
27
  import { AddressAutoCompleteProvider } from '../contexts/AutoCompleteContext';
29
28
  import { useScrollToAttributeError } from '../hooks/useScrollToAttributeError';
30
- import { useStyles } from './styles';
29
+ import styles from './RelationEditor.module.css';
31
30
  var areRelatedEntitiesChanged = function (initial, actual) {
32
31
  return initial.length !== actual.length || initial.some(function (_, i) { return initial[i] !== actual[i]; });
33
32
  };
@@ -36,7 +35,6 @@ var RelationEditor = function (_a) {
36
35
  var _b;
37
36
  var _c, _d;
38
37
  var connection = _a.connection, config = _a.config, mode = _a.mode, metadata = _a.metadata, highlightedError = _a.highlightedError;
39
- var styles = useStyles();
40
38
  var _e = connection.relation, relationType = _e.relationType, direction = _e.direction;
41
39
  var isNew = connection.isNew;
42
40
  var entityUri = getEntityUriFromConnection(connection);
@@ -227,7 +225,7 @@ var RelationEditor = function (_a) {
227
225
  var isMasked = showMasking && isEntityHasMaskedAttrValue;
228
226
  return (React.createElement(React.Fragment, null,
229
227
  React.createElement("div", { className: styles.editor },
230
- React.createElement(Typography, { className: styles.title }, isNew ? i18n.text('Add new item') : i18n.text('Edit item')),
228
+ React.createElement(Text, { className: styles.title }, isNew ? i18n.text('Add new item') : i18n.text('Edit item')),
231
229
  defaultRelationType === null && (React.createElement(ConnectionRelationTypeSelector, { className: styles.item, relation: connection.relation, inRelationTypes: inRelationTypes, outRelationTypes: outRelationTypes, onChange: onRelationTypeChange })),
232
230
  Boolean(relationType) && (React.createElement(ConfigPermissionsContextProvider, null,
233
231
  React.createElement("div", { ref: ref, className: errorClassName },
@@ -238,10 +236,10 @@ var RelationEditor = function (_a) {
238
236
  (connection.entity && isTempUri(connection.entity.entityUri)),
239
237
  _b)), entity: connection.entity || {}, entityTypesUris: entityTypesUris, globalSearchRequestOptions: globalSearchRequestOptions, mode: mode, onChange: onChangeEntity, onCreate: canCreateNewEntity ? onCreateEntity : undefined, metadata: metadata, attributeTypesSelectionStrategy: relationEditorAttributeTypesSelectionStrategy, isMasked: isMasked })))),
240
238
  React.createElement(HiddenAttributesContext.Provider, { value: emptyHiddenAttributes },
241
- React.createElement(EditModeAttributesList, { className: styles.item, attrTypes: firstLevelRelationAttrTypes, entity: attributeListEntity, showEmptyEditors: true, mode: mode, parentUri: relationUri, onAddAttributes: onAddAttributes, onChangeAttribute: onChangeAttribute, onDeleteAttribute: onDeleteAttribute }))))),
242
- React.createElement("div", { className: styles.actionButtons },
243
- React.createElement(Button, { onClick: onCancel }, i18n.text('Cancel')),
244
- React.createElement(Button, { color: "primary", disabled: !hasChanges, onClick: onApply }, i18n.text('Apply')))));
239
+ React.createElement(EditModeAttributesList, { className: styles.item, attrTypes: firstLevelRelationAttrTypes, entity: attributeListEntity, showEmptyEditors: true, mode: mode, parentUri: relationUri, onAddAttributes: onAddAttributes, onChangeAttribute: onChangeAttribute, onDeleteAttribute: onDeleteAttribute })))),
240
+ React.createElement("div", { className: styles.actionButtons },
241
+ React.createElement(Button, { design: "Transparent", onClick: onCancel }, i18n.text('Cancel')),
242
+ React.createElement(Button, { design: "Emphasized", disabled: !hasChanges, onClick: onApply }, i18n.text('Apply'))))));
245
243
  };
246
244
  export default withContext(ScrollToElementContext, function (contextValue, _a) {
247
245
  var connection = _a.connection;
@@ -0,0 +1,9 @@
1
+ const styles = {"editor":"RelationEditor-editor--ajWNL","title":"RelationEditor-title--rOGsW","item":"RelationEditor-item--vYyXo","dense":"RelationEditor-dense---NXeF","actionButtons":"RelationEditor-actionButtons--1LeIZ"};
2
+ if (typeof document !== 'undefined') {
3
+ const head = document.head || document.getElementsByTagName('head')[0]
4
+ const style = document.createElement('style');
5
+ style.type = 'text/css'
6
+ style.innerHTML = `.RelationEditor-editor--ajWNL{display:flex;flex-direction:column;overflow-x:hidden;padding:0 24px 16px}.RelationEditor-title--rOGsW{font-size:16px;margin:17px 0 24px}.RelationEditor-item--vYyXo{margin-bottom:20px}.RelationEditor-dense---NXeF{margin-bottom:0}.RelationEditor-actionButtons--1LeIZ{display:flex;gap:8px;justify-content:flex-end}`;
7
+ head.appendChild(style);
8
+ }
9
+ export default styles;
@@ -81,12 +81,13 @@ export var VerificationButton = function (_a) {
81
81
  var verificationAttributeTypeUris = useContextSelector(AttributesVerificationContext, function (context) { return context.verificationAttributeTypeUris; });
82
82
  var options = useMemo(function () {
83
83
  if (isNil(attributeType))
84
- return {};
84
+ return { forceVerify: true };
85
85
  var clientFilter = verificationType === InputVerificationAttribute.Email
86
86
  ? VerifyAttributesClientFilter.EmailVerifier
87
87
  : VerifyAttributesClientFilter.PhoneVerifier;
88
88
  return {
89
- clientFilter: clientFilter
89
+ clientFilter: clientFilter,
90
+ forceVerify: true
90
91
  };
91
92
  }, [attributeType, verificationType]);
92
93
  var verifySafePromise = useSafePromise();
@@ -1,17 +1,37 @@
1
1
  import React from 'react';
2
- import { MenuProps } from '@mui/material/Menu';
3
- import { DropDownMenuItem } from '../types';
4
- type Props<T> = {
2
+ export type DropDownMenuItem = {
3
+ text?: string;
4
+ id?: string;
5
+ /** A SAP icon name. */
6
+ icon?: string;
7
+ additionalText?: string;
8
+ tooltip?: string;
9
+ disabled?: boolean;
10
+ /** Only rendered inside a group, and controlled: the mark follows this prop, nothing else. */
11
+ checked?: boolean;
12
+ onClick?: (e: any) => void;
13
+ items?: DropDownMenuNode[];
14
+ };
15
+ export type DropDownMenuGroup = {
16
+ checkMode: 'Single' | 'Multiple';
17
+ items: DropDownMenuItem[];
18
+ };
19
+ export type DropDownMenuSeparator = {
20
+ separator: true;
21
+ };
22
+ export type DropDownMenuNode = DropDownMenuItem | DropDownMenuGroup | DropDownMenuSeparator;
23
+ /** Narrower than the legacy contract: `popoverProps` and `MenuItemRenderer` are MUI-only. */
24
+ export type DropDownMenuButtonProps = {
5
25
  className?: string;
6
26
  buttonComponent?: React.ElementType;
7
27
  buttonContent?: React.ReactNode;
8
- menuItems?: T[];
28
+ menuItems?: DropDownMenuNode[];
9
29
  buttonProps?: Record<string, unknown>;
10
- popoverProps?: Omit<MenuProps, 'open'>;
11
30
  menuId: string;
31
+ horizontalAlign?: 'Start' | 'Center' | 'End';
12
32
  onMenuOpen?: () => void;
13
33
  onMenuClose?: () => void;
14
- MenuItemRenderer?: (props: any) => React.ReactNode;
15
34
  };
16
- export declare function DropDownMenuButton<T = DropDownMenuItem>({ className, menuId, buttonComponent, buttonContent, menuItems, buttonProps, popoverProps, onMenuOpen, onMenuClose, MenuItemRenderer }: Props<T>): React.JSX.Element;
17
- export {};
35
+ export declare const isSeparatorNode: (node: DropDownMenuNode) => node is DropDownMenuSeparator;
36
+ export declare const isGroupNode: (node: DropDownMenuNode) => node is DropDownMenuGroup;
37
+ export declare function DropDownMenuButton({ className, menuId, buttonComponent, buttonContent, menuItems, buttonProps, horizontalAlign, onMenuOpen, onMenuClose }: DropDownMenuButtonProps): React.JSX.Element;
@@ -37,52 +37,114 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  return (mod && mod.__esModule) ? mod : { "default": mod };
38
38
  };
39
39
  Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.isGroupNode = exports.isSeparatorNode = void 0;
40
41
  exports.DropDownMenuButton = DropDownMenuButton;
41
42
  var react_1 = __importStar(require("react"));
42
43
  var classnames_1 = __importDefault(require("classnames"));
43
44
  var ramda_1 = require("ramda");
44
- var Button_1 = __importDefault(require("@mui/material/Button"));
45
- var Menu_1 = __importDefault(require("@mui/material/Menu"));
46
- var DefaultMenuItemRenderer_1 = require("./components/DefaultMenuItemRenderer");
45
+ var components_1 = require("@reltio/design/components");
47
46
  var useDidUpdateEffect_1 = require("../hooks/useDidUpdateEffect");
48
- var styles_1 = require("./styles");
47
+ var DropDownMenuButton_module_css_1 = __importDefault(require("./DropDownMenuButton.module.css"));
49
48
  var EMPTY_ARRAY = [];
50
49
  var EMPTY_OBJECT = {};
50
+ var isSeparatorNode = function (node) { return 'separator' in node; };
51
+ exports.isSeparatorNode = isSeparatorNode;
52
+ var isGroupNode = function (node) { return 'checkMode' in node; };
53
+ exports.isGroupNode = isGroupNode;
54
+ var buildMenu = function (nodes) {
55
+ var handlers = new Map();
56
+ var renderNode = function (node, key, checkMode) {
57
+ var _a, _b;
58
+ if ((0, exports.isSeparatorNode)(node)) {
59
+ return react_1.default.createElement(components_1.MenuSeparator, { key: key });
60
+ }
61
+ if ((0, exports.isGroupNode)(node)) {
62
+ return (react_1.default.createElement(components_1.MenuItemGroup, { key: key, checkMode: node.checkMode }, node.items.map(function (item, index) { return renderNode(item, "".concat(key, ".").concat(index), node.checkMode); })));
63
+ }
64
+ handlers.set(key, { item: node, checkMode: checkMode });
65
+ return (react_1.default.createElement(components_1.Tooltip, { key: key, title: node.tooltip },
66
+ react_1.default.createElement(components_1.MenuItem, { "data-item-key": key, "data-reltio-id": "reltio-search-menu-item".concat((_a = node.id) !== null && _a !== void 0 ? _a : ''),
67
+ // The wrapper describes the host, while the role sits on the shadow item, so the
68
+ // text has to come in as a name. The native `tooltip` would reach it too, but as
69
+ // a `title` that draws a second, unstyled bubble.
70
+ accessibleName: node.tooltip && [node.text, node.tooltip].filter(Boolean).join('. '), text: node.text, icon: node.icon, additionalText: node.additionalText, disabled: node.disabled, checked: node.checked }, (_b = node.items) === null || _b === void 0 ? void 0 : _b.map(function (child, index) { return renderNode(child, "".concat(key, ".").concat(index)); }))));
71
+ };
72
+ return { children: nodes.map(function (node, index) { return renderNode(node, String(index)); }), handlers: handlers };
73
+ };
51
74
  function DropDownMenuButton(_a) {
52
75
  var _b;
53
- var className = _a.className, menuId = _a.menuId, _c = _a.buttonComponent, buttonComponent = _c === void 0 ? Button_1.default : _c, buttonContent = _a.buttonContent, _d = _a.menuItems, menuItems = _d === void 0 ? EMPTY_ARRAY : _d, _e = _a.buttonProps, buttonProps = _e === void 0 ? EMPTY_OBJECT : _e, _f = _a.popoverProps, popoverProps = _f === void 0 ? EMPTY_OBJECT : _f, _g = _a.onMenuOpen, onMenuOpen = _g === void 0 ? ramda_1.identity : _g, _h = _a.onMenuClose, onMenuClose = _h === void 0 ? ramda_1.identity : _h, _j = _a.MenuItemRenderer, MenuItemRenderer = _j === void 0 ? DefaultMenuItemRenderer_1.DefaultMenuItemRenderer : _j;
76
+ var _c;
77
+ var className = _a.className, menuId = _a.menuId, _d = _a.buttonComponent, buttonComponent = _d === void 0 ? components_1.Button : _d, buttonContent = _a.buttonContent, _e = _a.menuItems, menuItems = _e === void 0 ? EMPTY_ARRAY : _e, _f = _a.buttonProps, buttonProps = _f === void 0 ? EMPTY_OBJECT : _f, _g = _a.horizontalAlign, horizontalAlign = _g === void 0 ? 'End' : _g, _h = _a.onMenuOpen, onMenuOpen = _h === void 0 ? ramda_1.identity : _h, _j = _a.onMenuClose, onMenuClose = _j === void 0 ? ramda_1.identity : _j;
54
78
  var ButtonComponent = buttonComponent;
55
- var styles = (0, styles_1.useStyles)();
56
- var buttonRef = (0, react_1.useRef)(null);
79
+ // The menu resolves the opener id on every use, so it survives a button node being replaced,
80
+ // which a ref captured in render would not. menuId is not unique on its own — the same menu is
81
+ // rendered per row in a few places.
82
+ var instanceId = (0, react_1.useId)();
83
+ var openerId = (_c = buttonProps.id) !== null && _c !== void 0 ? _c : "".concat(menuId, "-opener").concat(instanceId);
57
84
  var _k = (0, react_1.useState)(false), isOpen = _k[0], setIsOpen = _k[1];
58
- // onMenuOpen is fired synchronously in the click handler (below) so it wins the race against a
59
- // parent's onMouseLeave when the button lives in an unmounting hover overlay; onMenuClose stays
60
- // deferred to this effect so consumers can keep the button mounted (e.g. delete confirmation).
85
+ // onMenuOpen fires synchronously in the click handler to beat a parent's onMouseLeave in an
86
+ // unmounting hover overlay; onMenuClose stays deferred so consumers can keep the button mounted.
61
87
  (0, useDidUpdateEffect_1.useDidUpdateEffect)(function () {
62
88
  if (!isOpen) {
63
89
  onMenuClose();
64
90
  }
65
91
  }, [isOpen]);
92
+ var closeMenu = (0, react_1.useCallback)(function () { return setIsOpen(false); }, []);
66
93
  var handleToggle = (0, react_1.useCallback)(function (e) {
67
94
  e === null || e === void 0 ? void 0 : e.stopPropagation();
68
- var nextIsOpen = !isOpen;
69
- setIsOpen(nextIsOpen);
70
- if (nextIsOpen) {
71
- onMenuOpen();
95
+ if (isOpen) {
96
+ closeMenu();
97
+ return;
72
98
  }
73
- }, [isOpen, onMenuOpen]);
74
- var renderMenuItem = function (item, index) {
75
- return react_1.default.createElement(MenuItemRenderer, { item: item, key: index, onMenuClose: handleToggle });
76
- };
99
+ setIsOpen(true);
100
+ onMenuOpen();
101
+ }, [isOpen, closeMenu, onMenuOpen]);
102
+ // UI5 hands focus back to the opener while closing, so an item runs once that is over. Otherwise
103
+ // anything it focuses itself, such as a dialog without a focus trap, loses the focus right after.
104
+ var pendingClick = (0, react_1.useRef)(null);
105
+ var flushPendingClick = (0, react_1.useCallback)(function () {
106
+ var click = pendingClick.current;
107
+ pendingClick.current = null;
108
+ click === null || click === void 0 ? void 0 : click();
109
+ }, []);
110
+ // The popover closes a render later, so an opener that unmounts meanwhile — a hover overlay,
111
+ // for one — would otherwise swallow the action.
112
+ (0, react_1.useEffect)(function () { return flushPendingClick; }, [flushPendingClick]);
113
+ var handleMenuClose = (0, react_1.useCallback)(function () {
114
+ setIsOpen(false);
115
+ flushPendingClick();
116
+ }, [flushPendingClick]);
117
+ var _l = (0, react_1.useMemo)(function () { return buildMenu(menuItems); }, [menuItems]), children = _l.children, handlers = _l.handlers;
118
+ // A UI5 button builds the ARIA of its shadow <button> from this property; host attributes never
119
+ // reach it. `controls` is left out on purpose: the id would have to resolve from inside the shadow
120
+ // root, and an IDREF does not cross that boundary.
121
+ var accessibilityAttributes = (0, react_1.useMemo)(function () { return ({ hasPopup: 'menu', expanded: isOpen }); }, [isOpen]);
122
+ // UI5 reports the click on the root menu, submenus included, so the handler is looked up by key.
123
+ var handleItemClick = (0, react_1.useCallback)(function (e) {
124
+ var _a;
125
+ var entry = handlers.get(e.detail.item.dataset.itemKey);
126
+ if (!entry) {
127
+ return;
128
+ }
129
+ var item = entry.item, checkMode = entry.checkMode;
130
+ // Left alone, UI5 flips the mark itself and drifts away from the props. Cancelling the
131
+ // event takes both the mark and the closing back, at the cost of owning them here.
132
+ if (checkMode) {
133
+ e.preventDefault();
134
+ }
135
+ // A Multiple group is the one place where the menu stays open, so nothing has to wait.
136
+ if (checkMode === 'Multiple') {
137
+ (_a = item.onClick) === null || _a === void 0 ? void 0 : _a.call(item, e);
138
+ return;
139
+ }
140
+ if (item.onClick) {
141
+ pendingClick.current = function () { return item.onClick(e); };
142
+ }
143
+ if (checkMode === 'Single') {
144
+ closeMenu();
145
+ }
146
+ }, [handlers, closeMenu]);
77
147
  return (react_1.default.createElement(react_1.default.Fragment, null,
78
- react_1.default.createElement(ButtonComponent, __assign({ "data-reltio-id": "drop-down-menu-button", ref: buttonRef, className: (0, classnames_1.default)(styles.buttonRoot, (_b = {},
79
- _b[styles.selected] = isOpen,
80
- _b), className), onClick: handleToggle, "aria-pressed": isOpen, "aria-controls": isOpen ? menuId : undefined, "aria-haspopup": "true" }, buttonProps), buttonContent),
81
- react_1.default.createElement(Menu_1.default, __assign({ id: menuId, variant: "menu", open: isOpen, autoFocus: false, classes: { paper: styles.paper }, anchorEl: buttonRef.current, onClose: handleToggle, anchorOrigin: {
82
- vertical: 'bottom',
83
- horizontal: 'right'
84
- }, transformOrigin: {
85
- vertical: 'top',
86
- horizontal: 'right'
87
- }, elevation: 2, MenuListProps: { autoFocusItem: isOpen } }, popoverProps), menuItems.map(renderMenuItem))));
148
+ react_1.default.createElement(ButtonComponent, __assign({ "data-reltio-id": "drop-down-menu-button", id: openerId, className: (0, classnames_1.default)((_b = {}, _b[DropDownMenuButton_module_css_1.default.open] = isOpen, _b), className), onClick: handleToggle, accessibilityAttributes: accessibilityAttributes }, buttonProps), buttonContent),
149
+ react_1.default.createElement(components_1.Menu, { id: menuId, open: isOpen, opener: openerId, onClose: handleMenuClose, onItemClick: handleItemClick, horizontalAlign: horizontalAlign }, children)));
88
150
  }