UNPKG

saagie-ui

Version:

Saagie UI from Saagie Design System

150 lines (124 loc) 3.89 kB
import React, { useRef, useEffect, useState, } from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import { BreadcrumbItemContainer } from './BreadcrumbItemContainer'; import { BreadcrumbItem } from './BreadcrumbItem'; const propTypes = { /** * The component used for the root node. * Either a string to use a DOM element or a component. */ tag: PropTypes.elementType, children: PropTypes.node, className: PropTypes.string, defaultClassName: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]), /** * Array of dependencies that will trigger breadcrumb to recalculate which items to display */ deps: PropTypes.array, }; const defaultProps = { tag: 'div', children: '', className: '', defaultClassName: 'sui-m-breadcrumb', deps: [], }; export const Breadcrumb = ({ tag: Tag, children, className, defaultClassName, deps, ...attributes }) => { const containerRef = useRef(); const [itemsToDisplay, setItemsToDisplay] = useState(0); const classes = classnames( defaultClassName, className ); const getItemsToDisplay = (toDisplay) => { const container = containerRef.current; if (!container) { return toDisplay; } const items = [...container.querySelectorAll('.sui-m-breadcrumb__item')]; const itemsWidthArray = items.map((x) => x.offsetWidth); if (toDisplay >= items.length) { return toDisplay; } const containerWidth = container.offsetWidth; const itemsWidthArrayFiltered = toDisplay >= itemsWidthArray.length ? itemsWidthArray : [ ...itemsWidthArray.slice(0, 2), // First item + Ellipsis ...itemsWidthArray.slice(itemsWidthArray.length - toDisplay + 1), // Last items ]; const itemsWidth = itemsWidthArrayFiltered .reduce((acc, cur) => acc + cur, 0); if (itemsWidth < containerWidth) { return getItemsToDisplay(toDisplay + 1); } return toDisplay - 1; }; const updateItemsToDisplay = () => { const toDisplay = getItemsToDisplay(0); setItemsToDisplay(toDisplay >= 1 ? toDisplay : 1); }; useEffect(() => { updateItemsToDisplay(); }, [deps]); useEffect(() => { window.addEventListener('resize', updateItemsToDisplay); return () => { window.removeEventListener('resize', updateItemsToDisplay); }; }, []); const itemsList = React.Children.toArray(children); const itemsListDisplay = [ ...itemsList.slice(0, 1), <BreadcrumbItem>...</BreadcrumbItem>, ...itemsList.slice(1), ]; const shouldDisplayItem = (index) => { const isFirstItem = index === 0; const isLastItem = index >= itemsListDisplay.length - 1; const isEllipsisItem = index === 1; const isLastItemsToDisplay = index >= itemsListDisplay.length - itemsToDisplay + 1; // If only one item to display, show last item and ellipsis item if (itemsToDisplay <= 1) { return isLastItem || isEllipsisItem; } // Hide ellipsis if all items are displayed if (itemsList.length <= itemsToDisplay && isEllipsisItem) { return false; } // Show first item, ellipsis and last items if (isFirstItem || isEllipsisItem || isLastItemsToDisplay) { return true; } // Hide other items return false; }; return ( <div className={classes} {...attributes} ref={containerRef}> {itemsListDisplay.map((x, i) => ( <BreadcrumbItemContainer // eslint-disable-next-line react/no-array-index-key key={i} isHidden={!shouldDisplayItem(i)} style={{ minWidth: itemsToDisplay <= 1 && i >= itemsListDisplay.length - 1 ? 0 : null, }} > {x} </BreadcrumbItemContainer> ))} </div> ); }; Breadcrumb.propTypes = propTypes; Breadcrumb.defaultProps = defaultProps;