FrontPastel/node_modules/@mui/material/legacy/MenuList/MenuList.js

285 lines
11 KiB
JavaScript

'use client';
import _extends from "@babel/runtime/helpers/esm/extends";
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
import * as React from 'react';
import { isFragment } from 'react-is';
import PropTypes from 'prop-types';
import ownerDocument from '../utils/ownerDocument';
import List from '../List';
import getScrollbarSize from '../utils/getScrollbarSize';
import useForkRef from '../utils/useForkRef';
import useEnhancedEffect from '../utils/useEnhancedEffect';
import { jsx as _jsx } from "react/jsx-runtime";
function nextItem(list, item, disableListWrap) {
if (list === item) {
return list.firstChild;
}
if (item && item.nextElementSibling) {
return item.nextElementSibling;
}
return disableListWrap ? null : list.firstChild;
}
function previousItem(list, item, disableListWrap) {
if (list === item) {
return disableListWrap ? list.firstChild : list.lastChild;
}
if (item && item.previousElementSibling) {
return item.previousElementSibling;
}
return disableListWrap ? null : list.lastChild;
}
function textCriteriaMatches(nextFocus, textCriteria) {
if (textCriteria === undefined) {
return true;
}
var text = nextFocus.innerText;
if (text === undefined) {
// jsdom doesn't support innerText
text = nextFocus.textContent;
}
text = text.trim().toLowerCase();
if (text.length === 0) {
return false;
}
if (textCriteria.repeating) {
return text[0] === textCriteria.keys[0];
}
return text.indexOf(textCriteria.keys.join('')) === 0;
}
function moveFocus(list, currentFocus, disableListWrap, disabledItemsFocusable, traversalFunction, textCriteria) {
var wrappedOnce = false;
var nextFocus = traversalFunction(list, currentFocus, currentFocus ? disableListWrap : false);
while (nextFocus) {
// Prevent infinite loop.
if (nextFocus === list.firstChild) {
if (wrappedOnce) {
return false;
}
wrappedOnce = true;
}
// Same logic as useAutocomplete.js
var nextFocusDisabled = disabledItemsFocusable ? false : nextFocus.disabled || nextFocus.getAttribute('aria-disabled') === 'true';
if (!nextFocus.hasAttribute('tabindex') || !textCriteriaMatches(nextFocus, textCriteria) || nextFocusDisabled) {
// Move to the next element.
nextFocus = traversalFunction(list, nextFocus, disableListWrap);
} else {
nextFocus.focus();
return true;
}
}
return false;
}
/**
* A permanently displayed menu following https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/.
* It's exposed to help customization of the [`Menu`](/material-ui/api/menu/) component if you
* use it separately you need to move focus into the component manually. Once
* the focus is placed inside the component it is fully keyboard accessible.
*/
var MenuList = /*#__PURE__*/React.forwardRef(function MenuList(props, ref) {
var actions = props.actions,
_props$autoFocus = props.autoFocus,
autoFocus = _props$autoFocus === void 0 ? false : _props$autoFocus,
_props$autoFocusItem = props.autoFocusItem,
autoFocusItem = _props$autoFocusItem === void 0 ? false : _props$autoFocusItem,
children = props.children,
className = props.className,
_props$disabledItemsF = props.disabledItemsFocusable,
disabledItemsFocusable = _props$disabledItemsF === void 0 ? false : _props$disabledItemsF,
_props$disableListWra = props.disableListWrap,
disableListWrap = _props$disableListWra === void 0 ? false : _props$disableListWra,
onKeyDown = props.onKeyDown,
_props$variant = props.variant,
variant = _props$variant === void 0 ? 'selectedMenu' : _props$variant,
other = _objectWithoutProperties(props, ["actions", "autoFocus", "autoFocusItem", "children", "className", "disabledItemsFocusable", "disableListWrap", "onKeyDown", "variant"]);
var listRef = React.useRef(null);
var textCriteriaRef = React.useRef({
keys: [],
repeating: true,
previousKeyMatched: true,
lastTime: null
});
useEnhancedEffect(function () {
if (autoFocus) {
listRef.current.focus();
}
}, [autoFocus]);
React.useImperativeHandle(actions, function () {
return {
adjustStyleForScrollbar: function adjustStyleForScrollbar(containerElement, _ref) {
var direction = _ref.direction;
// Let's ignore that piece of logic if users are already overriding the width
// of the menu.
var noExplicitWidth = !listRef.current.style.width;
if (containerElement.clientHeight < listRef.current.clientHeight && noExplicitWidth) {
var scrollbarSize = "".concat(getScrollbarSize(ownerDocument(containerElement)), "px");
listRef.current.style[direction === 'rtl' ? 'paddingLeft' : 'paddingRight'] = scrollbarSize;
listRef.current.style.width = "calc(100% + ".concat(scrollbarSize, ")");
}
return listRef.current;
}
};
}, []);
var handleKeyDown = function handleKeyDown(event) {
var list = listRef.current;
var key = event.key;
/**
* @type {Element} - will always be defined since we are in a keydown handler
* attached to an element. A keydown event is either dispatched to the activeElement
* or document.body or document.documentElement. Only the first case will
* trigger this specific handler.
*/
var currentFocus = ownerDocument(list).activeElement;
if (key === 'ArrowDown') {
// Prevent scroll of the page
event.preventDefault();
moveFocus(list, currentFocus, disableListWrap, disabledItemsFocusable, nextItem);
} else if (key === 'ArrowUp') {
event.preventDefault();
moveFocus(list, currentFocus, disableListWrap, disabledItemsFocusable, previousItem);
} else if (key === 'Home') {
event.preventDefault();
moveFocus(list, null, disableListWrap, disabledItemsFocusable, nextItem);
} else if (key === 'End') {
event.preventDefault();
moveFocus(list, null, disableListWrap, disabledItemsFocusable, previousItem);
} else if (key.length === 1) {
var criteria = textCriteriaRef.current;
var lowerKey = key.toLowerCase();
var currTime = performance.now();
if (criteria.keys.length > 0) {
// Reset
if (currTime - criteria.lastTime > 500) {
criteria.keys = [];
criteria.repeating = true;
criteria.previousKeyMatched = true;
} else if (criteria.repeating && lowerKey !== criteria.keys[0]) {
criteria.repeating = false;
}
}
criteria.lastTime = currTime;
criteria.keys.push(lowerKey);
var keepFocusOnCurrent = currentFocus && !criteria.repeating && textCriteriaMatches(currentFocus, criteria);
if (criteria.previousKeyMatched && (keepFocusOnCurrent || moveFocus(list, currentFocus, false, disabledItemsFocusable, nextItem, criteria))) {
event.preventDefault();
} else {
criteria.previousKeyMatched = false;
}
}
if (onKeyDown) {
onKeyDown(event);
}
};
var handleRef = useForkRef(listRef, ref);
/**
* the index of the item should receive focus
* in a `variant="selectedMenu"` it's the first `selected` item
* otherwise it's the very first item.
*/
var activeItemIndex = -1;
// since we inject focus related props into children we have to do a lookahead
// to check if there is a `selected` item. We're looking for the last `selected`
// item and use the first valid item as a fallback
React.Children.forEach(children, function (child, index) {
if (! /*#__PURE__*/React.isValidElement(child)) {
if (activeItemIndex === index) {
activeItemIndex += 1;
if (activeItemIndex >= children.length) {
// there are no focusable items within the list.
activeItemIndex = -1;
}
}
return;
}
if (process.env.NODE_ENV !== 'production') {
if (isFragment(child)) {
console.error(["MUI: The Menu component doesn't accept a Fragment as a child.", 'Consider providing an array instead.'].join('\n'));
}
}
if (!child.props.disabled) {
if (variant === 'selectedMenu' && child.props.selected) {
activeItemIndex = index;
} else if (activeItemIndex === -1) {
activeItemIndex = index;
}
}
if (activeItemIndex === index && (child.props.disabled || child.props.muiSkipListHighlight || child.type.muiSkipListHighlight)) {
activeItemIndex += 1;
if (activeItemIndex >= children.length) {
// there are no focusable items within the list.
activeItemIndex = -1;
}
}
});
var items = React.Children.map(children, function (child, index) {
if (index === activeItemIndex) {
var newChildProps = {};
if (autoFocusItem) {
newChildProps.autoFocus = true;
}
if (child.props.tabIndex === undefined && variant === 'selectedMenu') {
newChildProps.tabIndex = 0;
}
return /*#__PURE__*/React.cloneElement(child, newChildProps);
}
return child;
});
return /*#__PURE__*/_jsx(List, _extends({
role: "menu",
ref: handleRef,
className: className,
onKeyDown: handleKeyDown,
tabIndex: autoFocus ? 0 : -1
}, other, {
children: items
}));
});
process.env.NODE_ENV !== "production" ? MenuList.propTypes /* remove-proptypes */ = {
// ┌────────────────────────────── Warning ──────────────────────────────┐
// │ These PropTypes are generated from the TypeScript type definitions. │
// │ To update them, edit the d.ts file and run `pnpm proptypes`. │
// └─────────────────────────────────────────────────────────────────────┘
/**
* If `true`, will focus the `[role="menu"]` container and move into tab order.
* @default false
*/
autoFocus: PropTypes.bool,
/**
* If `true`, will focus the first menuitem if `variant="menu"` or selected item
* if `variant="selectedMenu"`.
* @default false
*/
autoFocusItem: PropTypes.bool,
/**
* MenuList contents, normally `MenuItem`s.
*/
children: PropTypes.node,
/**
* @ignore
*/
className: PropTypes.string,
/**
* If `true`, will allow focus on disabled items.
* @default false
*/
disabledItemsFocusable: PropTypes.bool,
/**
* If `true`, the menu items will not wrap focus.
* @default false
*/
disableListWrap: PropTypes.bool,
/**
* @ignore
*/
onKeyDown: PropTypes.func,
/**
* The variant to use. Use `menu` to prevent selected items from impacting the initial focus
* and the vertical alignment relative to the anchor element.
* @default 'selectedMenu'
*/
variant: PropTypes.oneOf(['menu', 'selectedMenu'])
} : void 0;
export default MenuList;