UNPKG

@dnb/eufemia

Version:

DNB Eufemia Design System UI Library

184 lines (183 loc) 7.43 kB
"use strict"; "use client"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = MenuList; var _react = _interopRequireWildcard(require("react")); var _clsx = _interopRequireDefault(require("clsx")); var _MenuContext = require("./MenuContext.js"); var _useIsomorphicLayoutEffect = _interopRequireDefault(require("../../shared/helpers/useIsomorphicLayoutEffect.js")); var _jsxRuntime = require("react/jsx-runtime"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } function MenuList(props) { const { children, className, maxVisibleListItems, style, ...rest } = props; const context = (0, _MenuContext.useMenuContext)(); const ulRef = (0, _react.useRef)(null); const hasValidMaxVisibleListItems = typeof maxVisibleListItems === 'number' && Number.isFinite(maxVisibleListItems) && maxVisibleListItems > 0; const fallbackMaxHeight = hasValidMaxVisibleListItems ? `calc(var(--menu-action-min-height, 2.5rem) * ${maxVisibleListItems} + var(--menu-content-padding, 0.25rem) * 2)` : undefined; const [measuredMaxHeight, setMeasuredMaxHeight] = (0, _react.useState)(undefined); const measureMaxHeight = (0, _react.useCallback)(() => { if (!hasValidMaxVisibleListItems || style !== null && style !== void 0 && style.maxHeight) { setMeasuredMaxHeight(undefined); return; } const height = getVisibleMenuItemsHeight(ulRef.current, maxVisibleListItems); setMeasuredMaxHeight(height ? `${height}px` : undefined); }, [hasValidMaxVisibleListItems, maxVisibleListItems, style === null || style === void 0 ? void 0 : style.maxHeight]); (0, _useIsomorphicLayoutEffect.default)(() => { measureMaxHeight(); }, [children, measureMaxHeight]); (0, _useIsomorphicLayoutEffect.default)(() => { if (!hasValidMaxVisibleListItems || style !== null && style !== void 0 && style.maxHeight) { return undefined; } window.addEventListener('resize', measureMaxHeight); return () => { window.removeEventListener('resize', measureMaxHeight); }; }, [hasValidMaxVisibleListItems, measureMaxHeight, style === null || style === void 0 ? void 0 : style.maxHeight]); (0, _useIsomorphicLayoutEffect.default)(() => { if (context !== null && context !== void 0 && context.menuRef) { context.menuRef.current = ulRef.current; } }); const getNavigableItems = (0, _react.useCallback)(() => { const menuEl = ulRef.current; if (!menuEl) { return []; } return Array.from(menuEl.querySelectorAll('[role="menuitem"]:not([aria-disabled="true"])')); }, []); const focusByDomOrder = (0, _react.useCallback)(element => { if (!context) { return; } element.focus({ preventScroll: true }); const refIndex = context.itemRefs.current.findIndex(r => (r === null || r === void 0 ? void 0 : r.current) === element); if (refIndex !== -1) { context.setActiveIndex(refIndex); } }, [context]); const handleKeyDown = (0, _react.useCallback)(event => { if (!context) { return; } const items = getNavigableItems(); if (items.length === 0) { return; } let currentIdx = items.indexOf(document.activeElement); if (currentIdx === -1 && context.activeIndex >= 0) { const activeRef = context.itemRefs.current[context.activeIndex]; if (activeRef !== null && activeRef !== void 0 && activeRef.current) { currentIdx = items.indexOf(activeRef.current); } } switch (event.key) { case 'ArrowDown': { event.preventDefault(); event.stopPropagation(); if (currentIdx === -1) { focusByDomOrder(items[0]); } else { focusByDomOrder(items[(currentIdx + 1) % items.length]); } break; } case 'ArrowUp': { event.preventDefault(); event.stopPropagation(); if (currentIdx === -1) { focusByDomOrder(items[items.length - 1]); } else { focusByDomOrder(items[(currentIdx - 1 + items.length) % items.length]); } break; } case 'Home': case 'PageUp': { event.preventDefault(); event.stopPropagation(); focusByDomOrder(items[0]); break; } case 'End': case 'PageDown': { event.preventDefault(); event.stopPropagation(); focusByDomOrder(items[items.length - 1]); break; } case 'Escape': case 'Tab': { break; } default: { if (event.key.length === 1 && !event.ctrlKey && !event.metaKey) { const char = event.key.toLowerCase(); const startIdx = currentIdx === -1 ? 0 : (currentIdx + 1) % items.length; for (let i = 0; i < items.length; i++) { var _items$idx$textConten; const idx = (startIdx + i) % items.length; const text = (_items$idx$textConten = items[idx].textContent) === null || _items$idx$textConten === void 0 ? void 0 : _items$idx$textConten.trim().toLowerCase(); if (text !== null && text !== void 0 && text.startsWith(char)) { focusByDomOrder(items[idx]); break; } } } } } }, [context, getNavigableItems, focusByDomOrder]); const resolvedMaxHeight = style !== null && style !== void 0 && style.maxHeight ? undefined : measuredMaxHeight || fallbackMaxHeight; const listStyle = { ...(resolvedMaxHeight ? { maxHeight: resolvedMaxHeight, overflowY: 'auto' } : null), ...style }; return (0, _jsxRuntime.jsx)("ul", { ref: ulRef, role: "menu", tabIndex: -1, className: (0, _clsx.default)("dnb-menu__list dnb-no-focus", className), style: Object.keys(listStyle).length > 0 ? listStyle : undefined, onKeyDown: handleKeyDown, ...rest, children: children }); } function getVisibleMenuItemsHeight(ulElement, maxVisibleListItems) { if (!ulElement) { return null; } const items = Array.from(ulElement.children).filter(element => element instanceof HTMLElement); const firstVisibleItem = items[0]; const lastVisibleItem = items[maxVisibleListItems - 1]; if (!firstVisibleItem || !lastVisibleItem) { return null; } const contentHeight = Math.ceil(lastVisibleItem.offsetTop + lastVisibleItem.offsetHeight - firstVisibleItem.offsetTop); const computedStyle = getComputedStyle(ulElement); const paddingTop = parseFloat(computedStyle.paddingTop) || 0; const paddingBottom = parseFloat(computedStyle.paddingBottom) || 0; return contentHeight + paddingTop + paddingBottom; } //# sourceMappingURL=MenuList.js.map