1 | 'use client';
|
2 |
|
3 | import _extends from "@babel/runtime/helpers/esm/extends";
|
4 | import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
|
5 | import * as React from 'react';
|
6 | import { isFragment } from 'react-is';
|
7 | import PropTypes from 'prop-types';
|
8 | import ownerDocument from '../utils/ownerDocument';
|
9 | import List from '../List';
|
10 | import getScrollbarSize from '../utils/getScrollbarSize';
|
11 | import useForkRef from '../utils/useForkRef';
|
12 | import useEnhancedEffect from '../utils/useEnhancedEffect';
|
13 | import { jsx as _jsx } from "react/jsx-runtime";
|
14 | function nextItem(list, item, disableListWrap) {
|
15 | if (list === item) {
|
16 | return list.firstChild;
|
17 | }
|
18 | if (item && item.nextElementSibling) {
|
19 | return item.nextElementSibling;
|
20 | }
|
21 | return disableListWrap ? null : list.firstChild;
|
22 | }
|
23 | function previousItem(list, item, disableListWrap) {
|
24 | if (list === item) {
|
25 | return disableListWrap ? list.firstChild : list.lastChild;
|
26 | }
|
27 | if (item && item.previousElementSibling) {
|
28 | return item.previousElementSibling;
|
29 | }
|
30 | return disableListWrap ? null : list.lastChild;
|
31 | }
|
32 | function textCriteriaMatches(nextFocus, textCriteria) {
|
33 | if (textCriteria === undefined) {
|
34 | return true;
|
35 | }
|
36 | var text = nextFocus.innerText;
|
37 | if (text === undefined) {
|
38 |
|
39 | text = nextFocus.textContent;
|
40 | }
|
41 | text = text.trim().toLowerCase();
|
42 | if (text.length === 0) {
|
43 | return false;
|
44 | }
|
45 | if (textCriteria.repeating) {
|
46 | return text[0] === textCriteria.keys[0];
|
47 | }
|
48 | return text.indexOf(textCriteria.keys.join('')) === 0;
|
49 | }
|
50 | function moveFocus(list, currentFocus, disableListWrap, disabledItemsFocusable, traversalFunction, textCriteria) {
|
51 | var wrappedOnce = false;
|
52 | var nextFocus = traversalFunction(list, currentFocus, currentFocus ? disableListWrap : false);
|
53 | while (nextFocus) {
|
54 |
|
55 | if (nextFocus === list.firstChild) {
|
56 | if (wrappedOnce) {
|
57 | return false;
|
58 | }
|
59 | wrappedOnce = true;
|
60 | }
|
61 |
|
62 |
|
63 | var nextFocusDisabled = disabledItemsFocusable ? false : nextFocus.disabled || nextFocus.getAttribute('aria-disabled') === 'true';
|
64 | if (!nextFocus.hasAttribute('tabindex') || !textCriteriaMatches(nextFocus, textCriteria) || nextFocusDisabled) {
|
65 |
|
66 | nextFocus = traversalFunction(list, nextFocus, disableListWrap);
|
67 | } else {
|
68 | nextFocus.focus();
|
69 | return true;
|
70 | }
|
71 | }
|
72 | return false;
|
73 | }
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 | var MenuList = React.forwardRef(function MenuList(props, ref) {
|
82 | var actions = props.actions,
|
83 | _props$autoFocus = props.autoFocus,
|
84 | autoFocus = _props$autoFocus === void 0 ? false : _props$autoFocus,
|
85 | _props$autoFocusItem = props.autoFocusItem,
|
86 | autoFocusItem = _props$autoFocusItem === void 0 ? false : _props$autoFocusItem,
|
87 | children = props.children,
|
88 | className = props.className,
|
89 | _props$disabledItemsF = props.disabledItemsFocusable,
|
90 | disabledItemsFocusable = _props$disabledItemsF === void 0 ? false : _props$disabledItemsF,
|
91 | _props$disableListWra = props.disableListWrap,
|
92 | disableListWrap = _props$disableListWra === void 0 ? false : _props$disableListWra,
|
93 | onKeyDown = props.onKeyDown,
|
94 | _props$variant = props.variant,
|
95 | variant = _props$variant === void 0 ? 'selectedMenu' : _props$variant,
|
96 | other = _objectWithoutProperties(props, ["actions", "autoFocus", "autoFocusItem", "children", "className", "disabledItemsFocusable", "disableListWrap", "onKeyDown", "variant"]);
|
97 | var listRef = React.useRef(null);
|
98 | var textCriteriaRef = React.useRef({
|
99 | keys: [],
|
100 | repeating: true,
|
101 | previousKeyMatched: true,
|
102 | lastTime: null
|
103 | });
|
104 | useEnhancedEffect(function () {
|
105 | if (autoFocus) {
|
106 | listRef.current.focus();
|
107 | }
|
108 | }, [autoFocus]);
|
109 | React.useImperativeHandle(actions, function () {
|
110 | return {
|
111 | adjustStyleForScrollbar: function adjustStyleForScrollbar(containerElement, _ref) {
|
112 | var direction = _ref.direction;
|
113 |
|
114 |
|
115 | var noExplicitWidth = !listRef.current.style.width;
|
116 | if (containerElement.clientHeight < listRef.current.clientHeight && noExplicitWidth) {
|
117 | var scrollbarSize = "".concat(getScrollbarSize(ownerDocument(containerElement)), "px");
|
118 | listRef.current.style[direction === 'rtl' ? 'paddingLeft' : 'paddingRight'] = scrollbarSize;
|
119 | listRef.current.style.width = "calc(100% + ".concat(scrollbarSize, ")");
|
120 | }
|
121 | return listRef.current;
|
122 | }
|
123 | };
|
124 | }, []);
|
125 | var handleKeyDown = function handleKeyDown(event) {
|
126 | var list = listRef.current;
|
127 | var key = event.key;
|
128 | |
129 |
|
130 |
|
131 |
|
132 |
|
133 |
|
134 | var currentFocus = ownerDocument(list).activeElement;
|
135 | if (key === 'ArrowDown') {
|
136 |
|
137 | event.preventDefault();
|
138 | moveFocus(list, currentFocus, disableListWrap, disabledItemsFocusable, nextItem);
|
139 | } else if (key === 'ArrowUp') {
|
140 | event.preventDefault();
|
141 | moveFocus(list, currentFocus, disableListWrap, disabledItemsFocusable, previousItem);
|
142 | } else if (key === 'Home') {
|
143 | event.preventDefault();
|
144 | moveFocus(list, null, disableListWrap, disabledItemsFocusable, nextItem);
|
145 | } else if (key === 'End') {
|
146 | event.preventDefault();
|
147 | moveFocus(list, null, disableListWrap, disabledItemsFocusable, previousItem);
|
148 | } else if (key.length === 1) {
|
149 | var criteria = textCriteriaRef.current;
|
150 | var lowerKey = key.toLowerCase();
|
151 | var currTime = performance.now();
|
152 | if (criteria.keys.length > 0) {
|
153 |
|
154 | if (currTime - criteria.lastTime > 500) {
|
155 | criteria.keys = [];
|
156 | criteria.repeating = true;
|
157 | criteria.previousKeyMatched = true;
|
158 | } else if (criteria.repeating && lowerKey !== criteria.keys[0]) {
|
159 | criteria.repeating = false;
|
160 | }
|
161 | }
|
162 | criteria.lastTime = currTime;
|
163 | criteria.keys.push(lowerKey);
|
164 | var keepFocusOnCurrent = currentFocus && !criteria.repeating && textCriteriaMatches(currentFocus, criteria);
|
165 | if (criteria.previousKeyMatched && (keepFocusOnCurrent || moveFocus(list, currentFocus, false, disabledItemsFocusable, nextItem, criteria))) {
|
166 | event.preventDefault();
|
167 | } else {
|
168 | criteria.previousKeyMatched = false;
|
169 | }
|
170 | }
|
171 | if (onKeyDown) {
|
172 | onKeyDown(event);
|
173 | }
|
174 | };
|
175 | var handleRef = useForkRef(listRef, ref);
|
176 |
|
177 | |
178 |
|
179 |
|
180 |
|
181 |
|
182 | var activeItemIndex = -1;
|
183 |
|
184 |
|
185 |
|
186 | React.Children.forEach(children, function (child, index) {
|
187 | if (! React.isValidElement(child)) {
|
188 | if (activeItemIndex === index) {
|
189 | activeItemIndex += 1;
|
190 | if (activeItemIndex >= children.length) {
|
191 |
|
192 | activeItemIndex = -1;
|
193 | }
|
194 | }
|
195 | return;
|
196 | }
|
197 | if (process.env.NODE_ENV !== 'production') {
|
198 | if (isFragment(child)) {
|
199 | console.error(["MUI: The Menu component doesn't accept a Fragment as a child.", 'Consider providing an array instead.'].join('\n'));
|
200 | }
|
201 | }
|
202 | if (!child.props.disabled) {
|
203 | if (variant === 'selectedMenu' && child.props.selected) {
|
204 | activeItemIndex = index;
|
205 | } else if (activeItemIndex === -1) {
|
206 | activeItemIndex = index;
|
207 | }
|
208 | }
|
209 | if (activeItemIndex === index && (child.props.disabled || child.props.muiSkipListHighlight || child.type.muiSkipListHighlight)) {
|
210 | activeItemIndex += 1;
|
211 | if (activeItemIndex >= children.length) {
|
212 |
|
213 | activeItemIndex = -1;
|
214 | }
|
215 | }
|
216 | });
|
217 | var items = React.Children.map(children, function (child, index) {
|
218 | if (index === activeItemIndex) {
|
219 | var newChildProps = {};
|
220 | if (autoFocusItem) {
|
221 | newChildProps.autoFocus = true;
|
222 | }
|
223 | if (child.props.tabIndex === undefined && variant === 'selectedMenu') {
|
224 | newChildProps.tabIndex = 0;
|
225 | }
|
226 | return React.cloneElement(child, newChildProps);
|
227 | }
|
228 | return child;
|
229 | });
|
230 | return _jsx(List, _extends({
|
231 | role: "menu",
|
232 | ref: handleRef,
|
233 | className: className,
|
234 | onKeyDown: handleKeyDown,
|
235 | tabIndex: autoFocus ? 0 : -1
|
236 | }, other, {
|
237 | children: items
|
238 | }));
|
239 | });
|
240 | process.env.NODE_ENV !== "production" ? MenuList.propTypes = {
|
241 |
|
242 |
|
243 |
|
244 |
|
245 | |
246 |
|
247 |
|
248 |
|
249 | autoFocus: PropTypes.bool,
|
250 | |
251 |
|
252 |
|
253 |
|
254 |
|
255 | autoFocusItem: PropTypes.bool,
|
256 | |
257 |
|
258 |
|
259 | children: PropTypes.node,
|
260 | |
261 |
|
262 |
|
263 | className: PropTypes.string,
|
264 | |
265 |
|
266 |
|
267 |
|
268 | disabledItemsFocusable: PropTypes.bool,
|
269 | |
270 |
|
271 |
|
272 |
|
273 | disableListWrap: PropTypes.bool,
|
274 | |
275 |
|
276 |
|
277 | onKeyDown: PropTypes.func,
|
278 | |
279 |
|
280 |
|
281 |
|
282 |
|
283 | variant: PropTypes.oneOf(['menu', 'selectedMenu'])
|
284 | } : void 0;
|
285 | export default MenuList; |
\ | No newline at end of file |