UNPKG

monday-ui-react-core

Version:

Official monday.com UI resources for application development in React.js

139 lines (127 loc) 4.33 kB
import React, { useRef, forwardRef, useMemo } from "react"; import PropTypes from "prop-types"; import cx from "classnames"; import useMergeRefs from "../../hooks/useMergeRefs"; import MenuButton from "../MenuButton/MenuButton"; import "./ResponsiveList.scss"; import useElementsOverflowingIndex from "../../hooks/useElementsOverflowingIndex"; const DEFAULT_MINIMAL_MARGIN = 32; const EMPTY_ARRAY = []; const ResponsiveList = forwardRef( ( { id, className, rootClassName, children, menuButtonSize, paddingSize, dialogZIndex, dialogClassName, menuButtonClassName, resizeDebounceTime, menuButtonAriaLabel, menuButtonProps }, ref ) => { const componentRef = useRef(null); const mergedRef = useMergeRefs({ refs: [ref, componentRef] }); const index = useElementsOverflowingIndex({ ref: componentRef, children, paddingSize, resizeDebounceTime, ignoreLast: true }); const directChildren = useMemo(() => { if (index === -1) { return children; } const childrenArray = React.Children.toArray(children); return childrenArray.slice(0, index); }, [children, index]); const menuChildren = useMemo(() => { if (index === -1) { return EMPTY_ARRAY; } const childrenArray = React.Children.toArray(children); return childrenArray.slice(index, childrenArray.length); }, [children, index]); const hiddenChildren = useMemo(() => { const childrenArray = React.Children.toArray(children); return childrenArray.map(el => el?.props?.responsiveListPlaceholder || el); }, [children]); return ( <div className={cx("responsive-list--root", rootClassName)} id={id}> {index !== null && ( <div className={cx("responsive-list--wrapper", className)}> {directChildren} {!!menuChildren.length && ( <MenuButton componentClassName={cx("responsive-list-menu-button", menuButtonClassName)} size={menuButtonSize} openDialogComponentClassName={cx("responsive-list--menu-button-dialog", dialogClassName)} zIndex={dialogZIndex} ariaLabel={menuButtonAriaLabel} {...menuButtonProps} > <div className="responsive-list-menu-wrapper-flex">{menuChildren}</div> </MenuButton> )} </div> )} <div ref={mergedRef} className={cx("responsive-list--wrapper responsive-list--dummy", className)}> {hiddenChildren} <MenuButton componentClassName={cx("responsive-list-menu-button", menuButtonClassName)} size={menuButtonSize} openDialogComponentClassName={cx("responsive-list--menu-button-dialog", dialogClassName)} zIndex={dialogZIndex} ariaLabel={menuButtonAriaLabel} {...menuButtonProps} > <div className="responsive-list-menu-wrapper-flex" /> </MenuButton> </div> </div> ); } ); ResponsiveList.menuButtonSizes = MenuButton.sizes; ResponsiveList.propTypes = { id: PropTypes.string, className: PropTypes.string, menuButtonClassName: PropTypes.string, /** These attributes will be passed to the MenuButton */ menuButtonProps: PropTypes.object, menuButtonAriaLabel: PropTypes.string, rootClassName: PropTypes.string, dialogClassName: PropTypes.string, menuButtonSize: PropTypes.oneOf(Object.values(ResponsiveList.menuButtonSizes)), /** Amount of space to save between the menu button and the content */ paddingSize: PropTypes.number, dialogZIndex: PropTypes.number, /** * we use resize observer behind the scene to update the size, debounce the amount of callbacks (each callback may cause a reflow) */ resizeDebounceTime: PropTypes.number }; ResponsiveList.defaultProps = { id: "", className: "", dialogClassName: "", menuButtonClassName: "", rootClassName: "", menuButtonAriaLabel: "More Actions", menuButtonProps: {}, menuButtonSize: ResponsiveList.menuButtonSizes.SMALL, paddingSize: DEFAULT_MINIMAL_MARGIN, dialogZIndex: 9999, resizeDebounceTime: 0 }; export default ResponsiveList;