1 | import { __rest } from "tslib";
|
2 | import * as React from 'react';
|
3 | import styles from '@patternfly/react-styles/css/components/Menu/menu';
|
4 | import { css } from '@patternfly/react-styles';
|
5 | import topOffset from '@patternfly/react-tokens/dist/esm/c_menu_m_flyout__menu_top_offset';
|
6 | import rightOffset from '@patternfly/react-tokens/dist/esm/c_menu_m_flyout__menu_m_left_right_offset';
|
7 | import leftOffset from '@patternfly/react-tokens/dist/esm/c_menu_m_flyout__menu_left_offset';
|
8 | import ExternalLinkAltIcon from '@patternfly/react-icons/dist/esm/icons/external-link-alt-icon';
|
9 | import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon';
|
10 | import AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon';
|
11 | import CheckIcon from '@patternfly/react-icons/dist/esm/icons/check-icon';
|
12 | import { MenuContext, MenuItemContext } from './MenuContext';
|
13 | import { MenuItemAction } from './MenuItemAction';
|
14 | import { canUseDOM } from '../../helpers/util';
|
15 | import { useIsomorphicLayoutEffect } from '../../helpers/useIsomorphicLayout';
|
16 | const FlyoutContext = React.createContext({
|
17 | direction: 'right'
|
18 | });
|
19 | const MenuItemBase = (_a) => {
|
20 | var { children, className, itemId = null, to, isActive = null, isFavorited = null, isLoadButton = false, isLoading = false, flyoutMenu, direction, description = null, onClick = () => { }, component = 'button', isDisabled = false, isExternalLink = false, isSelected = null, icon, actions, onShowFlyout, drilldownMenu, isOnPath, innerRef } = _a, props = __rest(_a, ["children", "className", "itemId", "to", "isActive", "isFavorited", "isLoadButton", "isLoading", "flyoutMenu", "direction", "description", "onClick", "component", "isDisabled", "isExternalLink", "isSelected", "icon", "actions", "onShowFlyout", "drilldownMenu", "isOnPath", "innerRef"]);
|
21 | const { menuId, parentMenu, onSelect, onActionClick, activeItemId, selected, drilldownItemPath, onDrillIn, onDrillOut, flyoutRef, setFlyoutRef, disableHover } = React.useContext(MenuContext);
|
22 | const Component = (to ? 'a' : component);
|
23 | const [flyoutTarget, setFlyoutTarget] = React.useState(null);
|
24 | const flyoutContext = React.useContext(FlyoutContext);
|
25 | const [flyoutXDirection, setFlyoutXDirection] = React.useState(flyoutContext.direction);
|
26 | const ref = React.useRef();
|
27 | const flyoutVisible = ref === flyoutRef;
|
28 | const hasFlyout = flyoutMenu !== undefined;
|
29 | const showFlyout = (show) => {
|
30 | if (!flyoutVisible && show) {
|
31 | setFlyoutRef(ref);
|
32 | }
|
33 | else if (flyoutVisible && !show) {
|
34 | setFlyoutRef(null);
|
35 | }
|
36 | onShowFlyout && show && onShowFlyout();
|
37 | };
|
38 | useIsomorphicLayoutEffect(() => {
|
39 | if (hasFlyout && ref.current && canUseDOM) {
|
40 | const flyoutMenu = ref.current.lastElementChild;
|
41 | if (flyoutMenu && flyoutMenu.classList.contains(styles.menu)) {
|
42 | const origin = ref.current.getClientRects()[0];
|
43 | const rect = flyoutMenu.getClientRects()[0];
|
44 | if (origin && rect) {
|
45 | const spaceLeftLeft = origin.x - rect.width;
|
46 | const spaceLeftRight = window.innerWidth - origin.x - origin.width - rect.width;
|
47 | let xDir = flyoutXDirection;
|
48 | if (spaceLeftRight < 0 && xDir !== 'left') {
|
49 | setFlyoutXDirection('left');
|
50 | xDir = 'left';
|
51 | }
|
52 | else if (spaceLeftLeft < 0 && xDir !== 'right') {
|
53 | setFlyoutXDirection('right');
|
54 | xDir = 'right';
|
55 | }
|
56 | let xOffset = 0;
|
57 | if (spaceLeftLeft < 0 && spaceLeftRight < 0) {
|
58 | xOffset = xDir === 'right' ? -spaceLeftRight : -spaceLeftLeft;
|
59 | }
|
60 | if (xDir === 'left') {
|
61 | flyoutMenu.classList.add(styles.modifiers.left);
|
62 | flyoutMenu.style.setProperty(rightOffset.name, `-${xOffset}px`);
|
63 | }
|
64 | else {
|
65 | flyoutMenu.style.setProperty(leftOffset.name, `-${xOffset}px`);
|
66 | }
|
67 | const spaceLeftBot = window.innerHeight - origin.y - rect.height;
|
68 | const spaceLeftTop = window.innerHeight - rect.height;
|
69 | if (spaceLeftTop < 0 && spaceLeftBot < 0) {
|
70 |
|
71 |
|
72 | }
|
73 | else if (spaceLeftBot < 0) {
|
74 | flyoutMenu.style.setProperty(topOffset.name, `${spaceLeftBot}px`);
|
75 | }
|
76 | }
|
77 | }
|
78 | }
|
79 | }, [flyoutVisible, flyoutMenu]);
|
80 | React.useEffect(() => {
|
81 | setFlyoutXDirection(flyoutContext.direction);
|
82 | }, [flyoutContext]);
|
83 | React.useEffect(() => {
|
84 | if (flyoutTarget) {
|
85 | if (flyoutVisible) {
|
86 | const flyoutMenu = flyoutTarget.nextElementSibling;
|
87 | const flyoutItems = Array.from(flyoutMenu.getElementsByTagName('UL')[0].children).filter(el => !(el.classList.contains('pf-m-disabled') || el.classList.contains('pf-c-divider')));
|
88 | flyoutItems[0].firstChild.focus();
|
89 | }
|
90 | else {
|
91 | flyoutTarget.focus();
|
92 | }
|
93 | }
|
94 | }, [flyoutVisible, flyoutTarget]);
|
95 | const handleFlyout = (event) => {
|
96 | const key = event.key;
|
97 | const target = event.target;
|
98 | if (key === ' ' || key === 'Enter' || key === 'ArrowRight') {
|
99 | event.stopPropagation();
|
100 | if (!flyoutVisible) {
|
101 | showFlyout(true);
|
102 | setFlyoutTarget(target);
|
103 | }
|
104 | }
|
105 | if (key === 'Escape' || key === 'ArrowLeft') {
|
106 | if (flyoutVisible) {
|
107 | event.stopPropagation();
|
108 | showFlyout(false);
|
109 | }
|
110 | }
|
111 | };
|
112 | const onItemSelect = (event, onSelect) => {
|
113 |
|
114 | onSelect && onSelect(event, itemId);
|
115 |
|
116 | onClick && onClick(event);
|
117 | };
|
118 | const _isOnPath = (isOnPath && isOnPath) || (drilldownItemPath && drilldownItemPath.includes(itemId)) || false;
|
119 | let _drill;
|
120 | if (direction) {
|
121 | if (direction === 'down') {
|
122 | _drill = () => onDrillIn &&
|
123 | onDrillIn(menuId, typeof drilldownMenu === 'function'
|
124 | ? drilldownMenu().props.id
|
125 | : drilldownMenu.props.id, itemId);
|
126 | }
|
127 | else {
|
128 | _drill = () => onDrillOut && onDrillOut(parentMenu, itemId);
|
129 | }
|
130 | }
|
131 | let additionalProps = {};
|
132 | if (Component === 'a') {
|
133 | additionalProps = {
|
134 | href: to,
|
135 | 'aria-disabled': isDisabled ? true : null,
|
136 |
|
137 | disabled: null
|
138 | };
|
139 | }
|
140 | else if (Component === 'button') {
|
141 | additionalProps = {
|
142 | type: 'button'
|
143 | };
|
144 | }
|
145 | if (isOnPath) {
|
146 | additionalProps['aria-expanded'] = true;
|
147 | }
|
148 | else if (hasFlyout) {
|
149 | additionalProps['aria-haspopup'] = true;
|
150 | additionalProps['aria-expanded'] = flyoutVisible;
|
151 | }
|
152 | const getAriaCurrent = () => {
|
153 | if (isActive !== null) {
|
154 | if (isActive) {
|
155 | return 'page';
|
156 | }
|
157 | else {
|
158 | return null;
|
159 | }
|
160 | }
|
161 | else if (itemId !== null && activeItemId !== null) {
|
162 | return itemId === activeItemId;
|
163 | }
|
164 | return null;
|
165 | };
|
166 | const getIsSelected = () => {
|
167 | if (isSelected !== null) {
|
168 | return isSelected;
|
169 | }
|
170 | else if (selected !== null && itemId !== null) {
|
171 | return (Array.isArray(selected) && selected.includes(itemId)) || itemId === selected;
|
172 | }
|
173 | return false;
|
174 | };
|
175 | const onMouseOver = () => {
|
176 | if (disableHover) {
|
177 | return;
|
178 | }
|
179 | if (hasFlyout) {
|
180 | showFlyout(true);
|
181 | }
|
182 | else {
|
183 | setFlyoutRef(null);
|
184 | }
|
185 | };
|
186 | return (React.createElement("li", Object.assign({ className: css(styles.menuListItem, isDisabled && styles.modifiers.disabled, _isOnPath && styles.modifiers.currentPath, isLoadButton && styles.modifiers.load, isLoading && styles.modifiers.loading, className), onMouseOver: onMouseOver }, (flyoutMenu && { onKeyDown: handleFlyout }), { ref: ref, role: "none" }, props),
|
187 | React.createElement(Component, Object.assign({ tabIndex: -1, className: css(styles.menuItem, getIsSelected() && styles.modifiers.selected, className), "aria-current": getAriaCurrent(), disabled: isDisabled, role: "menuitem", ref: innerRef, onClick: (event) => {
|
188 | onItemSelect(event, onSelect);
|
189 | _drill && _drill();
|
190 | } }, additionalProps),
|
191 | React.createElement("span", { className: css(styles.menuItemMain) },
|
192 | direction === 'up' && (React.createElement("span", { className: css(styles.menuItemToggleIcon) },
|
193 | React.createElement(AngleLeftIcon, { "aria-hidden": true }))),
|
194 | icon && React.createElement("span", { className: css(styles.menuItemIcon) }, icon),
|
195 | React.createElement("span", { className: css(styles.menuItemText) }, children),
|
196 | isExternalLink && (React.createElement("span", { className: css(styles.menuItemExternalIcon) },
|
197 | React.createElement(ExternalLinkAltIcon, { "aria-hidden": true }))),
|
198 | (flyoutMenu || direction === 'down') && (React.createElement("span", { className: css(styles.menuItemToggleIcon) },
|
199 | React.createElement(AngleRightIcon, { "aria-hidden": true }))),
|
200 | getIsSelected() && (React.createElement("span", { className: css(styles.menuItemSelectIcon) },
|
201 | React.createElement(CheckIcon, { "aria-hidden": true })))),
|
202 | description && direction !== 'up' && (React.createElement("span", { className: css(styles.menuItemDescription) },
|
203 | React.createElement("span", null, description)))),
|
204 | flyoutVisible && (React.createElement(MenuContext.Provider, { value: { disableHover } },
|
205 | React.createElement(FlyoutContext.Provider, { value: { direction: flyoutXDirection } }, flyoutMenu))),
|
206 | typeof drilldownMenu === 'function' ? drilldownMenu() : drilldownMenu,
|
207 | React.createElement(MenuItemContext.Provider, { value: { itemId, isDisabled } },
|
208 | actions,
|
209 | isFavorited !== null && (React.createElement(MenuItemAction, { icon: "favorites", isFavorited: isFavorited, "aria-label": isFavorited ? 'starred' : 'not starred', onClick: event => onActionClick(event, itemId), tabIndex: -1, actionId: "fav" })))));
|
210 | };
|
211 | export const MenuItem = React.forwardRef((props, ref) => (React.createElement(MenuItemBase, Object.assign({}, props, { innerRef: ref }))));
|
212 | MenuItem.displayName = 'MenuItem';
|
213 |
|
\ | No newline at end of file |