@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.
- package/lib/molecules/action-menu-option.d.ts +8 -0
- package/lib/molecules/action-menu-option.js +129 -0
- package/lib/molecules/action-menu-option.js.map +1 -0
- package/lib/molecules/action-menu.d.ts +3 -1
- package/lib/molecules/action-menu.js +2 -12
- package/lib/molecules/action-menu.js.map +1 -1
- package/package.json +7 -6
- package/src/molecules/action-menu-option.tsx +110 -0
- package/src/molecules/action-menu.tsx +15 -44
|
@@ -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
|
|
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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
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.
|
|
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.
|
|
33
|
-
"@ltht-react/
|
|
34
|
-
"@ltht-react/
|
|
35
|
-
"@ltht-react/
|
|
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": "
|
|
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
|
|
2
|
-
import
|
|
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
|
|
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) =>
|
|
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
|