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 { getOUIAProps, getDefaultOUIAId } from '../../helpers';
|
6 | import { MenuContext } from './MenuContext';
|
7 | import { canUseDOM } from '../../helpers/util';
|
8 | import { KeyboardHandler } from '../../helpers';
|
9 | class MenuBase extends React.Component {
|
10 | constructor(props) {
|
11 | super(props);
|
12 | this.menuRef = React.createRef();
|
13 | this.activeMenu = null;
|
14 | this.state = {
|
15 | ouiaStateId: getDefaultOUIAId(Menu.displayName),
|
16 | searchInputValue: '',
|
17 | transitionMoveTarget: null,
|
18 | flyoutRef: null,
|
19 | disableHover: false
|
20 | };
|
21 | this.handleDrilldownTransition = (event) => {
|
22 | const current = this.menuRef.current;
|
23 | if (!current ||
|
24 | (current !== event.target.closest('.pf-c-menu') &&
|
25 | !Array.from(current.getElementsByClassName('pf-c-menu')).includes(event.target.closest('.pf-c-menu')))) {
|
26 | return;
|
27 | }
|
28 | if (this.state.transitionMoveTarget) {
|
29 | this.state.transitionMoveTarget.focus();
|
30 | this.setState({ transitionMoveTarget: null });
|
31 | }
|
32 | else {
|
33 | const nextMenu = current.querySelector('#' + this.props.activeMenu) || current || null;
|
34 | const nextTarget = Array.from(nextMenu.getElementsByTagName('UL')[0].children).filter(el => !(el.classList.contains('pf-m-disabled') || el.classList.contains('pf-c-divider')))[0].firstChild;
|
35 | nextTarget.focus();
|
36 | nextTarget.tabIndex = 0;
|
37 | }
|
38 | };
|
39 | this.handleExtraKeys = (event) => {
|
40 | const isDrilldown = this.props.containsDrilldown;
|
41 | const activeElement = document.activeElement;
|
42 | if (event.target.closest('.pf-c-menu') !== this.activeMenu &&
|
43 | !event.target.classList.contains('pf-c-breadcrumb__link')) {
|
44 | this.activeMenu = event.target.closest('.pf-c-menu');
|
45 | this.setState({ disableHover: true });
|
46 | }
|
47 | if (event.target.tagName === 'INPUT') {
|
48 | return;
|
49 | }
|
50 | const parentMenu = this.activeMenu;
|
51 | const key = event.key;
|
52 | const isFromBreadcrumb = activeElement.classList.contains('pf-c-breadcrumb__link') ||
|
53 | activeElement.classList.contains('pf-c-dropdown__toggle');
|
54 | if (key === ' ' || key === 'Enter') {
|
55 | event.preventDefault();
|
56 | if (isDrilldown && !isFromBreadcrumb) {
|
57 | const isDrillingOut = activeElement.closest('li').classList.contains('pf-m-current-path');
|
58 | if (isDrillingOut && parentMenu.parentElement.tagName === 'LI') {
|
59 | activeElement.tabIndex = -1;
|
60 | parentMenu.parentElement.firstChild.tabIndex = 0;
|
61 | this.setState({ transitionMoveTarget: parentMenu.parentElement.firstChild });
|
62 | }
|
63 | else {
|
64 | if (activeElement.nextElementSibling && activeElement.nextElementSibling.classList.contains('pf-c-menu')) {
|
65 | const childItems = Array.from(activeElement.nextElementSibling.getElementsByTagName('UL')[0].children).filter(el => !(el.classList.contains('pf-m-disabled') || el.classList.contains('pf-c-divider')));
|
66 | activeElement.tabIndex = -1;
|
67 | childItems[0].firstChild.tabIndex = 0;
|
68 | this.setState({ transitionMoveTarget: childItems[0].firstChild });
|
69 | }
|
70 | }
|
71 | }
|
72 | document.activeElement.click();
|
73 | }
|
74 | };
|
75 | this.createNavigableElements = () => {
|
76 | const isDrilldown = this.props.containsDrilldown;
|
77 | return isDrilldown
|
78 | ? Array.from(this.activeMenu.getElementsByTagName('UL')[0].children).filter(el => !(el.classList.contains('pf-m-disabled') || el.classList.contains('pf-c-divider')))
|
79 | : Array.from(this.menuRef.current.getElementsByTagName('LI')).filter(el => !(el.classList.contains('pf-m-disabled') || el.classList.contains('pf-c-divider')));
|
80 | };
|
81 | if (props.innerRef) {
|
82 | this.menuRef = props.innerRef;
|
83 | }
|
84 | }
|
85 | allowTabFirstItem() {
|
86 |
|
87 | const current = this.menuRef.current;
|
88 | if (current) {
|
89 | const first = current.querySelector('ul button, ul a');
|
90 | if (first) {
|
91 | first.tabIndex = 0;
|
92 | }
|
93 | }
|
94 | }
|
95 | componentDidMount() {
|
96 | if (this.context) {
|
97 | this.setState({ disableHover: this.context.disableHover });
|
98 | }
|
99 | if (canUseDOM) {
|
100 | window.addEventListener('transitionend', this.props.isRootMenu ? this.handleDrilldownTransition : null);
|
101 | }
|
102 | this.allowTabFirstItem();
|
103 | }
|
104 | componentWillUnmount() {
|
105 | if (canUseDOM) {
|
106 | window.removeEventListener('transitionend', this.handleDrilldownTransition);
|
107 | }
|
108 | }
|
109 | componentDidUpdate(prevProps) {
|
110 | if (prevProps.children !== this.props.children) {
|
111 | this.allowTabFirstItem();
|
112 | }
|
113 | }
|
114 | render() {
|
115 | const _a = this.props, { 'aria-label': ariaLabel, id, children, className, onSelect, selected = null, onActionClick, ouiaId, ouiaSafe, containsFlyout, isNavFlyout, containsDrilldown, isMenuDrilledIn, isPlain, isScrollable, drilldownItemPath, drilledInMenus, onDrillIn, onDrillOut, onGetMenuHeight, parentMenu = null, activeItemId = null,
|
116 |
|
117 | innerRef, isRootMenu, activeMenu } = _a,
|
118 |
|
119 | props = __rest(_a, ['aria-label', "id", "children", "className", "onSelect", "selected", "onActionClick", "ouiaId", "ouiaSafe", "containsFlyout", "isNavFlyout", "containsDrilldown", "isMenuDrilledIn", "isPlain", "isScrollable", "drilldownItemPath", "drilledInMenus", "onDrillIn", "onDrillOut", "onGetMenuHeight", "parentMenu", "activeItemId", "innerRef", "isRootMenu", "activeMenu"]);
|
120 | const _isMenuDrilledIn = isMenuDrilledIn || (drilledInMenus && drilledInMenus.includes(id)) || false;
|
121 | return (React.createElement(MenuContext.Provider, { value: {
|
122 | menuId: id,
|
123 | parentMenu: parentMenu || id,
|
124 | onSelect,
|
125 | onActionClick,
|
126 | activeItemId,
|
127 | selected,
|
128 | drilledInMenus,
|
129 | drilldownItemPath,
|
130 | onDrillIn,
|
131 | onDrillOut,
|
132 | onGetMenuHeight,
|
133 | flyoutRef: this.state.flyoutRef,
|
134 | setFlyoutRef: flyoutRef => this.setState({ flyoutRef }),
|
135 | disableHover: this.state.disableHover
|
136 | } },
|
137 | isRootMenu && (React.createElement(KeyboardHandler, { containerRef: this.menuRef || null, additionalKeyHandler: this.handleExtraKeys, createNavigableElements: this.createNavigableElements, isActiveElement: (element) => document.activeElement.parentElement === element ||
|
138 | (document.activeElement.closest('ol') && document.activeElement.closest('ol').firstChild === element), getFocusableElement: (navigableElement) => navigableElement.firstChild, noHorizontalArrowHandling: document.activeElement &&
|
139 | (document.activeElement.classList.contains('pf-c-breadcrumb__link') ||
|
140 | document.activeElement.classList.contains('pf-c-dropdown__toggle')), noEnterHandling: true, noSpaceHandling: true })),
|
141 | React.createElement("div", Object.assign({ id: id, className: css(styles.menu, isPlain && styles.modifiers.plain, isScrollable && styles.modifiers.scrollable, containsFlyout && styles.modifiers.flyout, isNavFlyout && styles.modifiers.nav, containsDrilldown && styles.modifiers.drilldown, _isMenuDrilledIn && styles.modifiers.drilledIn, className), "aria-label": ariaLabel, ref: this.menuRef }, getOUIAProps(Menu.displayName, ouiaId !== undefined ? ouiaId : this.state.ouiaStateId, ouiaSafe), props), children)));
|
142 | }
|
143 | }
|
144 | MenuBase.displayName = 'Menu';
|
145 | MenuBase.contextType = MenuContext;
|
146 | MenuBase.defaultProps = {
|
147 | ouiaSafe: true,
|
148 | isRootMenu: true,
|
149 | isPlain: false,
|
150 | isScrollable: false
|
151 | };
|
152 | export const Menu = React.forwardRef((props, ref) => (React.createElement(MenuBase, Object.assign({}, props, { innerRef: ref }))));
|
153 | Menu.displayName = 'Menu';
|
154 |
|
\ | No newline at end of file |