UNPKG

react-elegant-ui

Version:

Elegant UI components, made by BEM best practices for react

248 lines (247 loc) 9.96 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isGroup = exports.isAvailableItem = exports.getTextOfItem = exports.flattenItemsWithoutEmptyGroups = exports.flattenItems = exports.cnMenu = exports.Menu = void 0; var _react = _interopRequireWildcard(require("react")); var _classname = require("@bem-react/classname"); var _di = require("../../lib/di"); var _mergeRefs = require("../../lib/mergeRefs"); var _flatMap = require("../../lib/flatMap"); var _setRefValue = require("../../lib/setRefValue"); var _useIsomorphicLayoutEffect = require("../../hooks/useIsomorphicLayoutEffect"); require("./Menu.css"); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } var __assign = void 0 && (void 0).__assign || function () { __assign = Object.assign || function (t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __rest = void 0 && (void 0).__rest || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var isGroup = function (item) { return 'items' in item; }; exports.isGroup = isGroup; var getTextOfItem = function (item) { return 'textContent' in item ? item.textContent : item.content; }; exports.getTextOfItem = getTextOfItem; var isAvailableItem = function (item) { return item !== undefined && !item.disabled && !item.hidden; }; /** * Return flat item array * @param isRemoveHiddenGroups while `true` ignore items from hidden groups */ exports.isAvailableItem = isAvailableItem; var flattenItems = function (items, isRemoveHiddenGroups) { if (isRemoveHiddenGroups === void 0) { isRemoveHiddenGroups = false; } return (0, _flatMap.flatMap)(items, function (item) { if (!isGroup(item)) { return item; } return item.hidden && isRemoveHiddenGroups ? [] : flattenItems(item.items); }); }; /** * Return flat item array without items from hidden groups */ exports.flattenItems = flattenItems; var flattenItemsWithoutEmptyGroups = function (items) { return flattenItems(items, true); }; exports.flattenItemsWithoutEmptyGroups = flattenItemsWithoutEmptyGroups; var counter = function (init) { if (init === void 0) { init = 0; } return function () { return init++; }; }; var cnMenu = exports.cnMenu = (0, _classname.cn)('Menu'); var Menu = function (_a) { var className = _a.className, items = _a.items, disabled = _a.disabled, isRenderHidden = _a.isRenderHidden, isFocused = _a.isFocused, setFocus = _a.setFocus, cursorIndex = _a.cursorIndex, setCursorIndex = _a.setCursorIndex, onPick = _a.onPick, innerRef = _a.innerRef, cursorRefExternal = _a.cursorRef, cursorIdRefExternal = _a.cursorIdRef, addonBefore = _a.addonBefore, addonAfter = _a.addonAfter, props = __rest(_a, ["className", "items", "disabled", "isRenderHidden", "isFocused", "setFocus", "cursorIndex", "setCursorIndex", "onPick", "innerRef", "cursorRef", "cursorIdRef", "addonBefore", "addonAfter"]); var menuRef = (0, _react.useRef)(null); var cursorRef = (0, _react.useRef)(null); var flatItems = (0, _react.useMemo)(function () { return flattenItemsWithoutEmptyGroups(items); }, [items]); /** * Pick item pointed by cursor */ var pick = (0, _react.useCallback)(function () { if (disabled || cursorIndex === undefined || onPick === undefined) return; var item = flatItems[cursorIndex]; if (isAvailableItem(item)) { onPick(item.id, cursorIndex); } }, [disabled, cursorIndex, onPick, flatItems]); /** * Set new cursor index */ var setupCursor = (0, _react.useCallback)(function (index) { if (disabled || setCursorIndex === undefined) return; if (index === -1) { setCursorIndex(index); } else { var item = flatItems[index]; if (isAvailableItem(item)) { setCursorIndex(index); } } }, [disabled, setCursorIndex, flatItems]); // Scroll to cursor (0, _useIsomorphicLayoutEffect.useIsomorphicLayoutEffect)(function () { var scrollContainer = menuRef.current; var cursorElm = cursorRef.current; if (scrollContainer && cursorElm) { var itemStart = cursorElm.offsetTop - scrollContainer.offsetTop; var itemEnd = itemStart + cursorElm.offsetHeight; // If item fully fits in scrollContainer if (scrollContainer.clientHeight > cursorElm.offsetHeight) { var viewPortStart = scrollContainer.scrollTop; var viewPortEnd = viewPortStart + scrollContainer.clientHeight; if (viewPortStart > itemStart) { scrollContainer.scrollTop = itemStart; } else if (viewPortEnd < itemEnd) { scrollContainer.scrollTop = itemEnd - scrollContainer.clientHeight; } } else { scrollContainer.scrollTop = itemStart; } } }, [cursorIndex]); // Control cursor by focus (0, _react.useEffect)(function () { if (isFocused) { // Find and set cursor if (cursorIndex === undefined || cursorIndex === -1) { var cursor = flatItems.findIndex(isAvailableItem); setupCursor(cursor); } } else { // Reset cursor setupCursor(-1); } // must update only by change `isFocused` // eslint-disable-next-line react-hooks/exhaustive-deps }, [isFocused]); var setItemHover = function (hovered, index) { return function () { setupCursor(hovered && index !== undefined ? index : -1); }; }; // Keep actual `cursorIdRefExternal` value var refToCursorIdRef = (0, _react.useRef)(); refToCursorIdRef.current = cursorIdRefExternal; // Clean `cursorIdRefExternal` with unmount (0, _react.useEffect)(function () { return function () { if (refToCursorIdRef.current === undefined) return; (0, _setRefValue.setRefValue)(refToCursorIdRef.current, null); }; }, []); var cursorIdRef = (0, _react.useRef)(null); // Update `cursorIdRefExternal` (0, _react.useEffect)(function () { var _a; // Skip empty ref if (cursorIdRefExternal === undefined) return; // Clean when cursor is not exist if (cursorIndex === -1) { (0, _setRefValue.setRefValue)(cursorIdRefExternal, null); return; } var id = (_a = cursorIdRef === null || cursorIdRef === void 0 ? void 0 : cursorIdRef.current) !== null && _a !== void 0 ? _a : null; (0, _setRefValue.setRefValue)(cursorIdRefExternal, id); }, [cursorIdRefExternal, cursorIdRef, cursorIndex, items]); var renderItems = function (_a, itemIndexCounter, keyIndexCounter, isHiddenAll) { var Group = _a.Group, Item = _a.Item; if (isHiddenAll === void 0) { isHiddenAll = false; } return function (item) { var keyIndex = keyIndexCounter(); if (isGroup(item)) { var isHidden_1 = item.hidden || isHiddenAll; return isHidden_1 && !isRenderHidden ? undefined : ( /*#__PURE__*/_react.default.createElement(Group, { title: item.title, key: keyIndex, hidden: isHidden_1 }, item.items.map(renderItems({ Group: Group, Item: Item }, itemIndexCounter, keyIndexCounter, isHidden_1)))); } var itemIndex = itemIndexCounter(); var isCurrentCursor = cursorIndex !== undefined && cursorIndex === itemIndex; var isDisabled = item.disabled || disabled; var isHidden = item.hidden || isHiddenAll; return isHidden && !isRenderHidden ? undefined : ( /*#__PURE__*/_react.default.createElement(Item, { idRef: isCurrentCursor ? cursorIdRef : undefined, raw: item.raw, addonProps: item.addonProps, key: keyIndex, disabled: isDisabled, hidden: isHidden, cursor: isCurrentCursor, innerRef: isCurrentCursor ? (0, _mergeRefs.mergeRefsAsCallback)(cursorRef, cursorRefExternal) : undefined, onMouseEnter: setItemHover(true, itemIndex), onMouseLeave: setItemHover(false), onClick: isAvailableItem(item) ? pick : undefined }, item.content)); }; }; var itemIndexCounter = counter(); var keyIndexCounter = counter(); var _b = (0, _di.useComponentRegistry)(cnMenu()), Container = _b.Container, Group = _b.Group, Item = _b.Item; return /*#__PURE__*/_react.default.createElement("div", __assign({ role: "listbox", "aria-disabled": disabled }, props, { ref: (0, _mergeRefs.mergeRefsAsCallback)(menuRef, innerRef), className: cnMenu({ disabled: disabled, focused: isFocused }, [className]) }), addonBefore, /*#__PURE__*/_react.default.createElement(Container, null, items.map(renderItems({ Group: Group, Item: Item }, itemIndexCounter, keyIndexCounter))), addonAfter); }; exports.Menu = Menu; Menu.displayName = cnMenu();