UNPKG

@geneui/components

Version:

The Gene UI components library designed for BI tools

282 lines (275 loc) 12.8 kB
import { _ as _extends } from '../_rollupPluginBabelHelpers-e8fb2e5c.js'; import React__default, { forwardRef, useMemo, useState, useRef, useEffect, useCallback } from 'react'; import PropTypes from 'prop-types'; import { c as classnames } from '../index-031ff73c.js'; import { s as screenTypes } from '../configs-00612ce0.js'; import Option from '../Option/index.js'; import CustomScrollbar from '../Scrollbar/index.js'; import { s as styleInject } from '../style-inject.es-746bb8ed.js'; import '../dateValidation-67caec66.js'; import '../_commonjsHelpers-24198af3.js'; import 'react-dom'; import '../hooks/useDeviceType.js'; import '../hooks/useWindowSize.js'; import '../hooks/useDebounce.js'; import '../useEllipsisDetection-4d997d5d.js'; import '../Icon/index.js'; import '../index-6d7e99cd.js'; import '../tslib.es6-f211516f.js'; import '../GeneUIProvider/index.js'; const findDeep = function (data, indexStack) { let index = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; return index < indexStack.length ? findDeep(data[indexStack[index]].children, indexStack, index + 1) : data; }; var css_248z = "[data-gene-ui-version=\"2.16.5\"] .menu-items-wrapper{min-width:18rem;overflow:hidden;white-space:nowrap;width:100%}[data-gene-ui-version=\"2.16.5\"] .menu-items-wrapper>ul{display:flex;min-width:100%;transform:translateX(calc(var(--translate-x)*-1));transition:transform .3s}html[dir=rtl] .menu-items-wrapper>ul{transform:translateX(var(--translate-x))}[data-gene-ui-version=\"2.16.5\"] .menu-items-wrapper>ul.reversed>li:first-child{order:0}[data-gene-ui-version=\"2.16.5\"] .menu-items-wrapper>ul.reversed>li:nth-child(2){order:2}[data-gene-ui-version=\"2.16.5\"] .menu-items-wrapper>ul.reversed>li:nth-child(3){order:1}[data-gene-ui-version=\"2.16.5\"] .menu-items-wrapper>ul>li{display:flex;flex-direction:column;flex-shrink:0;width:100%}[data-gene-ui-version=\"2.16.5\"] .menu-items-wrapper .divider.type-horizontal{margin:0;width:100%}[data-gene-ui-version=\"2.16.5\"] .menu-items-wrapper .divider.type-horizontal.full-width{margin:1rem 0;width:100%}[data-gene-ui-version=\"2.16.5\"] .menu-items-wrapper .menu-item{align-items:center;cursor:pointer;display:flex;font:600 1.4rem/2rem var(--font-family);padding:1rem 1.4rem;transition:color .3s,background .3s}[data-gene-ui-version=\"2.16.5\"] .popover-positioner.mobile-view .menu-items-wrapper .menu-item{padding:1.4rem 2rem}[data-gene-ui-version=\"2.16.5\"] .menu-items-wrapper .menu-item.highlighted{color:var(--hero)}@media (hover:hover){[data-gene-ui-version=\"2.16.5\"] .menu-items-wrapper .menu-item:hover{background:rgba(var(--background-sc-rgb),.05)}[data-gene-ui-version=\"2.16.5\"] .menu-items-wrapper .menu-item.act-direction:hover{background:var(--hero);color:var(--hero-sc)}}[data-gene-ui-version=\"2.16.5\"] .menu-items-wrapper .menu-item .icon{margin:-.2rem 0}[data-gene-ui-version=\"2.16.5\"] .menu-items-wrapper .menu-item .icon.menu-custom-icon{margin-inline-end:1rem}[data-gene-ui-version=\"2.16.5\"] .menu-items-wrapper .menu-item.header{flex-direction:row-reverse}[data-gene-ui-version=\"2.16.5\"] .menu-items-wrapper .menu-text{flex:auto}[data-gene-ui-version=\"2.16.5\"] .menu-items-wrapper .menu-text .small-text{font-size:1.2rem;line-height:1.4rem;opacity:.7}"; styleInject(css_248z); const findHeaderByIndexStack = (data, indexStack) => { var _data$currentIndex; if (!data || !Array.isArray(data) || data.length === 0) { return null; } const [currentIndex, ...nextIndexStack] = indexStack; const nextData = (_data$currentIndex = data[currentIndex]) === null || _data$currentIndex === void 0 ? void 0 : _data$currentIndex.children; return indexStack.length === 1 ? data[currentIndex] : findHeaderByIndexStack(nextData, nextIndexStack); }; const Menu = /*#__PURE__*/forwardRef((_ref, ref) => { var _activeElementRef$cur2; let { data, onBack, toggle, onNext, onSelect, screenType, scrollToActiveElement, initialIndexStack, ...restProps } = _ref; const initialHeader = useMemo(() => initialIndexStack === null || initialIndexStack === void 0 ? void 0 : initialIndexStack.reduce((headerItem, stackIndex, index) => { const nextItem = headerItem ? headerItem[stackIndex] : data[stackIndex]; const isLast = index === initialIndexStack.length - 1; return isLast ? nextItem : nextItem.children; }, null), [initialIndexStack, data]); const [header, setHeader] = useState(initialHeader); const [indexStack, setIndexStack] = useState(initialIndexStack !== null && initialIndexStack !== void 0 ? initialIndexStack : []); const listRef = useRef(null); const activeElementRef = useRef(null); const [scrollTopGap, setScrollTopGap] = useState(0); const layer = useMemo(() => findDeep(data === null || data === void 0 ? void 0 : data.filter(item => item), indexStack), [data, indexStack]); // Scroll to active element const [activeElementTop, setActiveElementTop] = useState(null); const [allowScroll, setAllowScroll] = useState(false); const [isScrolled, setIsScrolled] = useState(false); const [scrollWithArrowKey, setScrollWithArrowKey] = useState(false); useEffect(() => { if (allowScroll || scrollWithArrowKey) { var _activeElementRef$cur; setActiveElementTop((activeElementRef === null || activeElementRef === void 0 ? void 0 : (_activeElementRef$cur = activeElementRef.current) === null || _activeElementRef$cur === void 0 ? void 0 : _activeElementRef$cur.offsetTop) - scrollTopGap); setAllowScroll(false); setScrollWithArrowKey(false); } }, [allowScroll, indexStack, activeElementRef === null || activeElementRef === void 0 ? void 0 : (_activeElementRef$cur2 = activeElementRef.current) === null || _activeElementRef$cur2 === void 0 ? void 0 : _activeElementRef$cur2.offsetTop, scrollTopGap, scrollWithArrowKey]); useEffect(() => { var _listRef$current; if (!isScrolled && (listRef === null || listRef === void 0 ? void 0 : (_listRef$current = listRef.current) === null || _listRef$current === void 0 ? void 0 : _listRef$current.container.firstChild.children.length) === layer.length) { setAllowScroll(true); setIsScrolled(true); } }); const handleClick = useCallback((e, item, isHeader, index) => { setWithSmoothScroll(false); setAllowScroll(true); setHoverIndex(null); setScrollTopGap(item !== null && item !== void 0 && item.scrollTopGap ? item === null || item === void 0 ? void 0 : item.scrollTopGap : 0); const newIndexStack = indexStack.slice(0, -1); if (isHeader) { setIndexStack(newIndexStack); onBack && onBack(e, item); setHeader(findHeaderByIndexStack(data, newIndexStack)); } else { const { onClick, children } = item; if (children) { setIndexStack([...indexStack, index]); setHeader(item); onNext && onNext(e, item); } else { onSelect && onSelect(e, item); } onClick && onClick(e, item); } }, [indexStack]); const isOtherLayoutExists = layer === null || layer === void 0 ? void 0 : layer.some(item => !!item.children); // key accessibility const [hoverIndex, setHoverIndex] = useState(null); const [allowHoverOnActiveElement, setAllowHoverOnActiveElement] = useState(false); const [withHeader, setWithHeader] = useState(false); const [hoveredHeader, setHoveredHeader] = useState(false); const [withSmoothScroll, setWithSmoothScroll] = useState(false); const handleMenuFocus = () => setHoverIndex(0); const keyDownHandler = e => { setWithSmoothScroll(true); switch (e.key) { case 'ArrowDown': { if (hoveredHeader) { setHoverIndex(0); setHoveredHeader(false); } else { setHoverIndex(prev => prev >= layer.length - 1 ? layer.length - 1 : prev + 1); setScrollWithArrowKey(true); } break; } case 'ArrowUp': { if (hoverIndex <= 0) { setHoverIndex(null); if (withHeader) { setHoveredHeader(true); } else { setScrollWithArrowKey(true); } } else { setHoverIndex(prev => prev - 1); setScrollWithArrowKey(true); } break; } case 'Enter': { setAllowHoverOnActiveElement(true); const selectedItem = layer[hoverIndex]; if (selectedItem) { handleClick(e, selectedItem, false, hoverIndex); setHoverIndex(0); } if (hoveredHeader) handleClick(e, header, true); break; } case 'Tab': case 'Escape': toggle && toggle(false); break; } }; const omitLayoutProps = obj => { let result = obj; if (obj !== null && obj !== void 0 && obj.maxHeight) { const { maxHeight, ...restProps } = result; result = restProps; } if (obj !== null && obj !== void 0 && obj.scrollTopGap) { const { scrollTopGap, ...restProps } = result; result = restProps; } return result; }; const contentListMemo = useMemo(() => layer === null || layer === void 0 ? void 0 : layer.map((item, index) => { const itemProps = omitLayoutProps(item); if (allowHoverOnActiveElement && item.active) { setHoverIndex(index); setAllowHoverOnActiveElement(false); } return item.component || /*#__PURE__*/React__default.createElement(Option, _extends({ key: "".concat(item.header).concat(index), title: item.title, onClick: e => handleClick(e, item, false, index), forwardMark: !!item.children, screenType: screenType }, scrollToActiveElement && item.active && allowScroll || index === hoverIndex ? { forwardedRef: activeElementRef } : {}, itemProps, { className: classnames({ hovered: index === hoverIndex }, itemProps.className) })); }), [indexStack, layer, hoverIndex]); const headerMemo = useMemo(() => { const headerProps = omitLayoutProps(header); setWithHeader(!!headerProps); return header && /*#__PURE__*/React__default.createElement(Option, _extends({ onClick: e => handleClick(e, header, true), icon: "bc-icon-arrow-left", border: "bottom", className: classnames({ hovered: hoveredHeader }), screenType: screenType }, headerProps, { title: header.title })); }, [header, screenType, header === null || header === void 0 ? void 0 : header.maxHeight, indexStack, hoveredHeader]); return /*#__PURE__*/React__default.createElement("div", _extends({ className: "menu-items-wrapper popover-top-bottom-padding", onKeyDown: keyDownHandler, onFocus: handleMenuFocus, tabIndex: 0, ref: ref }, restProps), /*#__PURE__*/React__default.createElement("ul", { style: { '--translate-x': "".concat(indexStack.length * 100, "%") } }, indexStack.map((_, index) => /*#__PURE__*/React__default.createElement("li", { key: index })), /*#__PURE__*/React__default.createElement("li", null, headerMemo, header !== null && header !== void 0 && header.maxHeight ? /*#__PURE__*/React__default.createElement(CustomScrollbar, { style: { height: "".concat(header.maxHeight, "px") }, ref: listRef, scrollTop: activeElementTop, withSmoothScroll: withSmoothScroll }, contentListMemo) : contentListMemo), isOtherLayoutExists && /*#__PURE__*/React__default.createElement("li", null))); }); Menu.defaultProps = { screenType: screenTypes[0] }; Menu.propTypes = { /** * This is an array where you send menu options, if you send a component, * then your component will be rendered, and if parameters, they will be * used for the atoms/Option component.<br/> * Add <code>maxHeight</code> to data to add scrollbar with maxHeight(px) */ data: PropTypes.arrayOf(PropTypes.shape({ component: PropTypes.node, maxHeight: PropTypes.number, ...Option.propTypes })), /** * Fires event when user click on header elements; * (event: Event, item: Object) => void */ onBack: PropTypes.func, /** * Fires event when user click on option elements; * (event: Event, item: Object) => void */ onNext: PropTypes.func, /** * Fires event when user click on elements; * (event: Event, item: Object) => void */ onSelect: PropTypes.func, /** * Controls screen type */ screenType: PropTypes.oneOf(screenTypes), /** * auto scroll to activeElement */ scrollToActiveElement: PropTypes.bool, /** * Initial selected options indexes array */ initialIndexStack: PropTypes.arrayOf(PropTypes.string) }; export { Menu as default };