saagie-ui
Version:
Saagie UI from Saagie Design System
150 lines (124 loc) • 3.89 kB
JavaScript
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;