@geneui/components
Version:
The Gene UI components library designed for BI tools
282 lines (275 loc) • 12.8 kB
JavaScript
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 };