@ltht-react/menu 2.0.171 → 2.0.173

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.
@@ -0,0 +1,8 @@
1
+ import { FC } from 'react';
2
+ import { ActionMenuOption as IActionMenuOption } from './action-menu';
3
+ interface Props extends IActionMenuOption {
4
+ idPrefix: string;
5
+ index: number;
6
+ }
7
+ declare const ActionMenuOption: FC<Props>;
8
+ export default ActionMenuOption;
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
+ return new (P || (P = Promise))(function (resolve, reject) {
16
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
17
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
18
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
19
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
20
+ });
21
+ };
22
+ var __generator = (this && this.__generator) || function (thisArg, body) {
23
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
24
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
25
+ function verb(n) { return function (v) { return step([n, v]); }; }
26
+ function step(op) {
27
+ if (f) throw new TypeError("Generator is already executing.");
28
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
29
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
30
+ if (y = 0, t) op = [op[0] & 2, t.value];
31
+ switch (op[0]) {
32
+ case 0: case 1: t = op; break;
33
+ case 4: _.label++; return { value: op[1], done: false };
34
+ case 5: _.label++; y = op[1]; op = [0]; continue;
35
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
36
+ default:
37
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
38
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
39
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
40
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
41
+ if (t[2]) _.ops.pop();
42
+ _.trys.pop(); continue;
43
+ }
44
+ op = body.call(thisArg, _);
45
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
46
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
47
+ }
48
+ };
49
+ var __rest = (this && this.__rest) || function (s, e) {
50
+ var t = {};
51
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
52
+ t[p] = s[p];
53
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
54
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
55
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
56
+ t[p[i]] = s[p[i]];
57
+ }
58
+ return t;
59
+ };
60
+ var __importDefault = (this && this.__importDefault) || function (mod) {
61
+ return (mod && mod.__esModule) ? mod : { "default": mod };
62
+ };
63
+ Object.defineProperty(exports, "__esModule", { value: true });
64
+ var jsx_runtime_1 = require("react/jsx-runtime");
65
+ var react_1 = require("react");
66
+ var icon_1 = __importDefault(require("@ltht-react/icon"));
67
+ var utils_1 = require("@ltht-react/utils");
68
+ var hooks_1 = require("@ltht-react/hooks");
69
+ var menu_1 = require("./menu");
70
+ // Utility function to generate consistent IDs
71
+ var generateActionId = function (idPrefix, text, index) {
72
+ var textId = (0, utils_1.stringToHtmlId)(text);
73
+ return "".concat(idPrefix, "action-menu-item-").concat(textId, "-").concat(index);
74
+ };
75
+ // Memoized icon renderer to prevent unnecessary re-renders
76
+ var IconRenderer = (0, react_1.memo)(function (_a) {
77
+ var icon = _a.icon;
78
+ if (!icon)
79
+ return null;
80
+ return (0, jsx_runtime_1.jsx)(icon_1.default, __assign({}, icon));
81
+ });
82
+ IconRenderer.displayName = 'IconRenderer';
83
+ var ActionMenuOption = function (_a) {
84
+ var idPrefix = _a.idPrefix, index = _a.index, text = _a.text, leftIcon = _a.leftIcon, rightIcon = _a.rightIcon, actions = _a.actions, _b = _a.exitFullScreenOnClick, exitFullScreenOnClick = _b === void 0 ? false : _b, clickHandler = _a.clickHandler, onClick = _a.onClick, _c = _a.disabled, disabled = _c === void 0 ? false : _c, rest = __rest(_a, ["idPrefix", "index", "text", "leftIcon", "rightIcon", "actions", "exitFullScreenOnClick", "clickHandler", "onClick", "disabled"]);
85
+ var _d = (0, hooks_1.useFullScreen)(), isFullscreen = _d.isFullscreen, exitFullScreen = _d.exitFullScreen;
86
+ // Memoize the action ID to prevent recalculation on every render
87
+ var actionMenuItemId = (0, react_1.useMemo)(function () { return generateActionId(idPrefix, text, index); }, [idPrefix, text, index]);
88
+ // Memoize icons to prevent unnecessary re-renders
89
+ var leftIconElement = (0, react_1.useMemo)(function () { return (0, jsx_runtime_1.jsx)(IconRenderer, { icon: leftIcon }); }, [leftIcon]);
90
+ var rightIconElement = (0, react_1.useMemo)(function () { return (0, jsx_runtime_1.jsx)(IconRenderer, { icon: rightIcon }); }, [rightIcon]);
91
+ var handleOnClick = (0, react_1.useCallback)(function (e) { return __awaiter(void 0, void 0, void 0, function () {
92
+ return __generator(this, function (_a) {
93
+ switch (_a.label) {
94
+ case 0:
95
+ if (disabled) {
96
+ e.preventDefault();
97
+ return [2 /*return*/];
98
+ }
99
+ if (!clickHandler && !onClick) {
100
+ return [2 /*return*/];
101
+ }
102
+ if (!(exitFullScreenOnClick && isFullscreen)) return [3 /*break*/, 2];
103
+ return [4 /*yield*/, exitFullScreen()];
104
+ case 1:
105
+ _a.sent();
106
+ _a.label = 2;
107
+ case 2:
108
+ // Execute the appropriate handler
109
+ if (clickHandler) {
110
+ clickHandler();
111
+ }
112
+ else if (onClick) {
113
+ onClick(e);
114
+ }
115
+ return [2 /*return*/];
116
+ }
117
+ });
118
+ }); }, [disabled, clickHandler, onClick, exitFullScreenOnClick, isFullscreen, exitFullScreen]);
119
+ // Determine if this is a submenu (has actions)
120
+ var isSubmenu = actions && actions.length > 0;
121
+ // Common props for both Menu and MenuItem
122
+ var commonProps = (0, react_1.useMemo)(function () { return (__assign({ id: actionMenuItemId, 'data-testid': actionMenuItemId, label: text, leftIcon: leftIconElement, rightIcon: rightIconElement, disabled: disabled }, rest)); }, [actionMenuItemId, text, leftIconElement, rightIconElement, disabled, rest]);
123
+ if (isSubmenu) {
124
+ return ((0, jsx_runtime_1.jsx)(menu_1.Menu, __assign({}, commonProps, { "aria-label": "".concat(text, " submenu"), "aria-expanded": "false", children: actions.map(function (action, actionIndex) { return ((0, jsx_runtime_1.jsx)(ActionMenuOption, __assign({ idPrefix: "".concat(actionMenuItemId, "_"), index: actionIndex }, action), "".concat(actionMenuItemId, "_menu_item_").concat(actionIndex))); }) })));
125
+ }
126
+ return (0, jsx_runtime_1.jsx)(menu_1.MenuItem, __assign({}, commonProps, { onClick: handleOnClick, "aria-label": text }));
127
+ };
128
+ exports.default = ActionMenuOption;
129
+ //# sourceMappingURL=action-menu-option.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-menu-option.js","sourceRoot":"","sources":["../../src/molecules/action-menu-option.tsx"],"names":[],"mappings":"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"}
@@ -1,10 +1,11 @@
1
1
  import { FC, HTMLAttributes } from 'react';
2
2
  import { IconProps } from '@ltht-react/icon';
3
3
  import { ButtonProps } from '@ltht-react/button';
4
- interface IProps extends HTMLAttributes<HTMLButtonElement> {
4
+ interface IProps<T extends HTMLElement = HTMLElement> extends HTMLAttributes<HTMLButtonElement> {
5
5
  actions: ActionMenuOption[];
6
6
  menuButtonOptions?: IconButtonMenuProps | ButtonMenuProps;
7
7
  disabled?: boolean;
8
+ root?: React.MutableRefObject<T | null>;
8
9
  }
9
10
  interface IconButtonMenuProps {
10
11
  type: 'icon';
@@ -23,6 +24,7 @@ export interface ActionMenuOption extends HTMLAttributes<HTMLButtonElement> {
23
24
  leftIcon?: IconProps;
24
25
  rightIcon?: IconProps;
25
26
  disabled?: boolean;
27
+ exitFullScreenOnClick?: boolean;
26
28
  actions?: ActionMenuOption[];
27
29
  }
28
30
  export declare const DefaultTrigger: IconButtonMenuProps;
@@ -27,9 +27,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
27
27
  Object.defineProperty(exports, "__esModule", { value: true });
28
28
  exports.DefaultTrigger = void 0;
29
29
  var jsx_runtime_1 = require("react/jsx-runtime");
30
- var icon_1 = __importDefault(require("@ltht-react/icon"));
31
- var utils_1 = require("@ltht-react/utils");
32
30
  var menu_1 = require("./menu");
31
+ var action_menu_option_1 = __importDefault(require("./action-menu-option"));
33
32
  exports.DefaultTrigger = {
34
33
  type: 'icon',
35
34
  iconProps: {
@@ -40,16 +39,7 @@ exports.DefaultTrigger = {
40
39
  var ActionMenu = function (_a) {
41
40
  var _b = _a.id, id = _b === void 0 ? 'action-menu-button' : _b, actions = _a.actions, disabled = _a.disabled, _c = _a.menuButtonOptions, menuButtonOptions = _c === void 0 ? __assign(__assign({}, exports.DefaultTrigger), { disabled: disabled }) : _c, rest = __rest(_a, ["id", "actions", "disabled", "menuButtonOptions"]);
42
41
  var menuItemIdPrefix = id ? "".concat(id, "-") : '';
43
- return ((0, jsx_runtime_1.jsx)(menu_1.Menu, __assign({ rootTrigger: menuButtonOptions, "data-testid": id }, rest, { children: actions.map(function (action, index) { return renderAction(menuItemIdPrefix, action, index); }) })));
44
- };
45
- var renderAction = function (idPrefix, _a, index) {
46
- var text = _a.text, leftIcon = _a.leftIcon, rightIcon = _a.rightIcon, clickHandler = _a.clickHandler, onClick = _a.onClick, actions = _a.actions, rest = __rest(_a, ["text", "leftIcon", "rightIcon", "clickHandler", "onClick", "actions"]);
47
- var textId = (0, utils_1.stringToHtmlId)(text);
48
- var actionMenuItemId = "".concat(idPrefix, "action-menu-item-").concat(textId, "-").concat(index);
49
- if (!!(actions === null || actions === void 0 ? void 0 : actions.length) && actions.length > 0) {
50
- return ((0, jsx_runtime_1.jsx)(menu_1.Menu, __assign({ id: actionMenuItemId, "data-testid": actionMenuItemId, label: text, leftIcon: leftIcon ? (0, jsx_runtime_1.jsx)(icon_1.default, __assign({}, leftIcon)) : null, rightIcon: rightIcon ? (0, jsx_runtime_1.jsx)(icon_1.default, __assign({}, rightIcon)) : null }, rest, { children: actions.map(function (action, index) { return renderAction(actionMenuItemId, action, index); }) }), actionMenuItemId));
51
- }
52
- return ((0, jsx_runtime_1.jsx)(menu_1.MenuItem, __assign({ id: actionMenuItemId, "data-testid": actionMenuItemId, label: text, leftIcon: leftIcon ? (0, jsx_runtime_1.jsx)(icon_1.default, __assign({}, leftIcon)) : null, rightIcon: rightIcon ? (0, jsx_runtime_1.jsx)(icon_1.default, __assign({}, rightIcon)) : null, onClick: clickHandler !== null && clickHandler !== void 0 ? clickHandler : onClick }, rest), actionMenuItemId));
42
+ return ((0, jsx_runtime_1.jsx)(menu_1.Menu, __assign({ rootTrigger: menuButtonOptions, "data-testid": id }, rest, { children: actions.map(function (action, index) { return ((0, jsx_runtime_1.jsx)(action_menu_option_1.default, __assign({ idPrefix: menuItemIdPrefix, index: index }, action), "".concat(menuItemIdPrefix, "_menu_item_").concat(index))); }) })));
53
43
  };
54
44
  exports.default = ActionMenu;
55
45
  //# sourceMappingURL=action-menu.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"action-menu.js","sourceRoot":"","sources":["../../src/molecules/action-menu.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,0DAAkD;AAClD,2CAAkD;AAGlD,+BAAuC;AA8B1B,QAAA,cAAc,GAAwB;IACjD,IAAI,EAAE,MAAM;IACZ,SAAS,EAAE;QACT,IAAI,EAAE,mBAAmB;QACzB,IAAI,EAAE,OAAO;KACd;CACF,CAAA;AAED,IAAM,UAAU,GAAe,UAAC,EAM/B;IALC,IAAA,UAAyB,EAAzB,EAAE,mBAAG,oBAAoB,KAAA,EACzB,OAAO,aAAA,EACP,QAAQ,cAAA,EACR,yBAAmD,EAAnD,iBAAiB,yCAAQ,sBAAc,KAAE,QAAQ,UAAA,QAAE,EAChD,IAAI,cALuB,kDAM/B,CADQ;IAEP,IAAM,gBAAgB,GAAG,EAAE,CAAC,CAAC,CAAC,UAAG,EAAE,MAAG,CAAC,CAAC,CAAC,EAAE,CAAA;IAE3C,OAAO,CACL,uBAAC,WAAI,aAAC,WAAW,EAAE,iBAAiB,iBAAe,EAAE,IAAM,IAAI,cAC5D,OAAO,CAAC,GAAG,CAAC,UAAC,MAAM,EAAE,KAAK,IAAK,OAAA,YAAY,CAAC,gBAAgB,EAAE,MAAM,EAAE,KAAK,CAAC,EAA7C,CAA6C,CAAC,IACzE,CACR,CAAA;AACH,CAAC,CAAA;AAED,IAAM,YAAY,GAAG,UACnB,QAAgB,EAChB,EAAwF,EACxF,KAAa;IADX,IAAA,IAAI,UAAA,EAAE,QAAQ,cAAA,EAAE,SAAS,eAAA,EAAE,YAAY,kBAAA,EAAE,OAAO,aAAA,EAAE,OAAO,aAAA,EAAK,IAAI,cAApE,uEAAsE,CAAF;IAGpE,IAAM,MAAM,GAAG,IAAA,sBAAc,EAAC,IAAI,CAAC,CAAA;IACnC,IAAM,gBAAgB,GAAG,UAAG,QAAQ,8BAAoB,MAAM,cAAI,KAAK,CAAE,CAAA;IAEzE,IAAI,CAAC,CAAC,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,CAAA,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5C,OAAO,CACL,uBAAC,WAAI,aAEH,EAAE,EAAE,gBAAgB,iBACP,gBAAgB,EAC7B,KAAK,EAAE,IAAI,EACX,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,uBAAC,cAAI,eAAK,QAAQ,EAAI,CAAC,CAAC,CAAC,IAAI,EAClD,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,uBAAC,cAAI,eAAK,SAAS,EAAI,CAAC,CAAC,CAAC,IAAI,IACjD,IAAI,cAEP,OAAO,CAAC,GAAG,CAAC,UAAC,MAAM,EAAE,KAAK,IAAK,OAAA,YAAY,CAAC,gBAAgB,EAAE,MAAM,EAAE,KAAK,CAAC,EAA7C,CAA6C,CAAC,KARzE,gBAAgB,CAShB,CACR,CAAA;IACH,CAAC;IAED,OAAO,CACL,uBAAC,eAAQ,aAEP,EAAE,EAAE,gBAAgB,iBACP,gBAAgB,EAC7B,KAAK,EAAE,IAAI,EACX,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,uBAAC,cAAI,eAAK,QAAQ,EAAI,CAAC,CAAC,CAAC,IAAI,EAClD,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,uBAAC,cAAI,eAAK,SAAS,EAAI,CAAC,CAAC,CAAC,IAAI,EACrD,OAAO,EAAE,YAAY,aAAZ,YAAY,cAAZ,YAAY,GAAI,OAAO,IAC5B,IAAI,GAPH,gBAAgB,CAQrB,CACH,CAAA;AACH,CAAC,CAAA;AAED,kBAAe,UAAU,CAAA"}
1
+ {"version":3,"file":"action-menu.js","sourceRoot":"","sources":["../../src/molecules/action-menu.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,+BAA6B;AAC7B,4EAA6C;AAgChC,QAAA,cAAc,GAAwB;IACjD,IAAI,EAAE,MAAM;IACZ,SAAS,EAAE;QACT,IAAI,EAAE,mBAAmB;QACzB,IAAI,EAAE,OAAO;KACd;CACF,CAAA;AAED,IAAM,UAAU,GAAe,UAAC,EAM/B;IALC,IAAA,UAAyB,EAAzB,EAAE,mBAAG,oBAAoB,KAAA,EACzB,OAAO,aAAA,EACP,QAAQ,cAAA,EACR,yBAAmD,EAAnD,iBAAiB,yCAAQ,sBAAc,KAAE,QAAQ,UAAA,QAAE,EAChD,IAAI,cALuB,kDAM/B,CADQ;IAEP,IAAM,gBAAgB,GAAG,EAAE,CAAC,CAAC,CAAC,UAAG,EAAE,MAAG,CAAC,CAAC,CAAC,EAAE,CAAA;IAE3C,OAAO,CACL,uBAAC,WAAI,aAAC,WAAW,EAAE,iBAAiB,iBAAe,EAAE,IAAM,IAAI,cAC5D,OAAO,CAAC,GAAG,CAAC,UAAC,MAAM,EAAE,KAAK,IAAK,OAAA,CAC9B,uBAAC,4BAAU,aAET,QAAQ,EAAE,gBAAgB,EAC1B,KAAK,EAAE,KAAK,IACR,MAAM,GAHL,UAAG,gBAAgB,wBAAc,KAAK,CAAE,CAI7C,CACH,EAP+B,CAO/B,CAAC,IACG,CACR,CAAA;AACH,CAAC,CAAA;AAED,kBAAe,UAAU,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ltht-react/menu",
3
- "version": "2.0.171",
3
+ "version": "2.0.173",
4
4
  "description": "ltht-react styled Menu component.",
5
5
  "author": "LTHT",
6
6
  "homepage": "",
@@ -29,11 +29,12 @@
29
29
  "@emotion/react": "^11.0.0",
30
30
  "@emotion/styled": "^11.0.0",
31
31
  "@floating-ui/react": "^0.27.15",
32
- "@ltht-react/button": "^2.0.171",
33
- "@ltht-react/icon": "^2.0.171",
34
- "@ltht-react/styles": "^2.0.171",
35
- "@ltht-react/utils": "^2.0.171",
32
+ "@ltht-react/button": "^2.0.173",
33
+ "@ltht-react/hooks": "^2.0.173",
34
+ "@ltht-react/icon": "^2.0.173",
35
+ "@ltht-react/styles": "^2.0.173",
36
+ "@ltht-react/utils": "^2.0.173",
36
37
  "react": "^18.2.0"
37
38
  },
38
- "gitHead": "13fe4e114f4d2c06abb58682d94a4ebe6801945e"
39
+ "gitHead": "ae3f8693ad59993242b1268375fe95ae3db4640a"
39
40
  }
@@ -0,0 +1,110 @@
1
+ import { useMemo, MouseEvent, memo, FC, useCallback } from 'react'
2
+ import Icon, { IconProps } from '@ltht-react/icon'
3
+ import { stringToHtmlId } from '@ltht-react/utils'
4
+ import { useFullScreen } from '@ltht-react/hooks'
5
+ import { ActionMenuOption as IActionMenuOption } from './action-menu'
6
+ import { Menu, MenuItem } from './menu'
7
+
8
+ interface Props extends IActionMenuOption {
9
+ idPrefix: string
10
+ index: number
11
+ }
12
+
13
+ // Utility function to generate consistent IDs
14
+ const generateActionId = (idPrefix: string, text: string, index: number): string => {
15
+ const textId = stringToHtmlId(text)
16
+ return `${idPrefix}action-menu-item-${textId}-${index}`
17
+ }
18
+
19
+ // Memoized icon renderer to prevent unnecessary re-renders
20
+ const IconRenderer = memo<{ icon?: IconProps }>(({ icon }) => {
21
+ if (!icon) return null
22
+ return <Icon {...icon} />
23
+ })
24
+
25
+ IconRenderer.displayName = 'IconRenderer'
26
+
27
+ const ActionMenuOption: FC<Props> = ({
28
+ idPrefix,
29
+ index,
30
+ text,
31
+ leftIcon,
32
+ rightIcon,
33
+ actions,
34
+ exitFullScreenOnClick = false,
35
+ clickHandler,
36
+ onClick,
37
+ disabled = false,
38
+ ...rest
39
+ }) => {
40
+ const { isFullscreen, exitFullScreen } = useFullScreen()
41
+
42
+ // Memoize the action ID to prevent recalculation on every render
43
+ const actionMenuItemId = useMemo(() => generateActionId(idPrefix, text, index), [idPrefix, text, index])
44
+
45
+ // Memoize icons to prevent unnecessary re-renders
46
+ const leftIconElement = useMemo(() => <IconRenderer icon={leftIcon} />, [leftIcon])
47
+
48
+ const rightIconElement = useMemo(() => <IconRenderer icon={rightIcon} />, [rightIcon])
49
+
50
+ const handleOnClick = useCallback(
51
+ async (e: MouseEvent<HTMLButtonElement>) => {
52
+ if (disabled) {
53
+ e.preventDefault()
54
+ return
55
+ }
56
+
57
+ if (!clickHandler && !onClick) {
58
+ return
59
+ }
60
+
61
+ if (exitFullScreenOnClick && isFullscreen) {
62
+ await exitFullScreen()
63
+ }
64
+
65
+ // Execute the appropriate handler
66
+ if (clickHandler) {
67
+ clickHandler()
68
+ } else if (onClick) {
69
+ onClick(e)
70
+ }
71
+ },
72
+ [disabled, clickHandler, onClick, exitFullScreenOnClick, isFullscreen, exitFullScreen]
73
+ )
74
+
75
+ // Determine if this is a submenu (has actions)
76
+ const isSubmenu = actions && actions.length > 0
77
+
78
+ // Common props for both Menu and MenuItem
79
+ const commonProps = useMemo(
80
+ () => ({
81
+ id: actionMenuItemId,
82
+ 'data-testid': actionMenuItemId,
83
+ label: text,
84
+ leftIcon: leftIconElement,
85
+ rightIcon: rightIconElement,
86
+ disabled,
87
+ ...rest,
88
+ }),
89
+ [actionMenuItemId, text, leftIconElement, rightIconElement, disabled, rest]
90
+ )
91
+
92
+ if (isSubmenu) {
93
+ return (
94
+ <Menu {...commonProps} aria-label={`${text} submenu`} aria-expanded="false">
95
+ {actions.map((action, actionIndex) => (
96
+ <ActionMenuOption
97
+ key={`${actionMenuItemId}_menu_item_${actionIndex}`}
98
+ idPrefix={`${actionMenuItemId}_`}
99
+ index={actionIndex}
100
+ {...action}
101
+ />
102
+ ))}
103
+ </Menu>
104
+ )
105
+ }
106
+
107
+ return <MenuItem {...commonProps} onClick={handleOnClick} aria-label={text} />
108
+ }
109
+
110
+ export default ActionMenuOption
@@ -1,14 +1,15 @@
1
- import { FC, HTMLAttributes, ReactNode } from 'react'
2
- import Icon, { IconProps } from '@ltht-react/icon'
3
- import { stringToHtmlId } from '@ltht-react/utils'
1
+ import { FC, HTMLAttributes } from 'react'
2
+ import { IconProps } from '@ltht-react/icon'
4
3
  import { ButtonProps } from '@ltht-react/button'
5
4
 
6
- import { Menu, MenuItem } from './menu'
5
+ import { Menu } from './menu'
6
+ import MenuOption from './action-menu-option'
7
7
 
8
- interface IProps extends HTMLAttributes<HTMLButtonElement> {
8
+ interface IProps<T extends HTMLElement = HTMLElement> extends HTMLAttributes<HTMLButtonElement> {
9
9
  actions: ActionMenuOption[]
10
10
  menuButtonOptions?: IconButtonMenuProps | ButtonMenuProps
11
11
  disabled?: boolean
12
+ root?: React.MutableRefObject<T | null>
12
13
  }
13
14
 
14
15
  interface IconButtonMenuProps {
@@ -30,6 +31,7 @@ export interface ActionMenuOption extends HTMLAttributes<HTMLButtonElement> {
30
31
  leftIcon?: IconProps
31
32
  rightIcon?: IconProps
32
33
  disabled?: boolean
34
+ exitFullScreenOnClick?: boolean
33
35
  actions?: ActionMenuOption[]
34
36
  }
35
37
 
@@ -52,47 +54,16 @@ const ActionMenu: FC<IProps> = ({
52
54
 
53
55
  return (
54
56
  <Menu rootTrigger={menuButtonOptions} data-testid={id} {...rest}>
55
- {actions.map((action, index) => renderAction(menuItemIdPrefix, action, index))}
57
+ {actions.map((action, index) => (
58
+ <MenuOption
59
+ key={`${menuItemIdPrefix}_menu_item_${index}`}
60
+ idPrefix={menuItemIdPrefix}
61
+ index={index}
62
+ {...action}
63
+ />
64
+ ))}
56
65
  </Menu>
57
66
  )
58
67
  }
59
68
 
60
- const renderAction = (
61
- idPrefix: string,
62
- { text, leftIcon, rightIcon, clickHandler, onClick, actions, ...rest }: ActionMenuOption,
63
- index: number
64
- ): ReactNode => {
65
- const textId = stringToHtmlId(text)
66
- const actionMenuItemId = `${idPrefix}action-menu-item-${textId}-${index}`
67
-
68
- if (!!actions?.length && actions.length > 0) {
69
- return (
70
- <Menu
71
- key={actionMenuItemId}
72
- id={actionMenuItemId}
73
- data-testid={actionMenuItemId}
74
- label={text}
75
- leftIcon={leftIcon ? <Icon {...leftIcon} /> : null}
76
- rightIcon={rightIcon ? <Icon {...rightIcon} /> : null}
77
- {...rest}
78
- >
79
- {actions.map((action, index) => renderAction(actionMenuItemId, action, index))}
80
- </Menu>
81
- )
82
- }
83
-
84
- return (
85
- <MenuItem
86
- key={actionMenuItemId}
87
- id={actionMenuItemId}
88
- data-testid={actionMenuItemId}
89
- label={text}
90
- leftIcon={leftIcon ? <Icon {...leftIcon} /> : null}
91
- rightIcon={rightIcon ? <Icon {...rightIcon} /> : null}
92
- onClick={clickHandler ?? onClick}
93
- {...rest}
94
- />
95
- )
96
- }
97
-
98
69
  export default ActionMenu